2 * Portions copyright 2004-2007 Aaron Boodman
3 * Copyright 2015 Ketmar Dark <ketmar@ketmar.no-ip.org>
4 * Contributors: See contributors list in install.rdf and CREDITS
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * Note that this license applies only to the Greasemonkey extension source
14 * files, not to the user scripts which it runs. User scripts are licensed
15 * separately by their authors.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 * The above copyright notice and this permission notice shall be included in all
26 * copies or substantial portions of the Software.
28 ////////////////////////////////////////////////////////////////////////////////
29 let {GuerillaXmlHttpReqester
} = require("sbapi/xmlhttprequest");
30 let {ScriptStorage
} = require("sbapi/scriptstorage");
31 let {openTab
} = require("sbapi/opentab");
32 let {buildRelFile
} = require("scriptcache");
35 ////////////////////////////////////////////////////////////////////////////////
36 function fileReadBinary (fl
) {
37 let istream
= Cc
["@mozilla.org/network/file-input-stream;1"].createInstance(Ci
.nsIFileInputStream
);
38 istream
.init(fl
, -1, -1, false);
39 let bstream
= Cc
["@mozilla.org/binaryinputstream;1"].createInstance(Ci
.nsIBinaryInputStream
);
40 bstream
.setInputStream(istream
);
41 let bytes
= bstream
.readBytes(bstream
.available());
47 ////////////////////////////////////////////////////////////////////////////////
48 let ioSvc
= Cc
["@mozilla.org/network/io-service;1"].getService(Ci
.nsIIOService
);
50 function uriFromUrl (url
, base
) {
52 if (typeof(base
) === "string") {
53 baseUri
= uriFromUrl(base
);
58 return ioSvc
.newURI(url
, null, baseUri
);
65 let newFileURI
= Cc
["@mozilla.org/network/io-service;1"].getService(Ci
.nsIIOService
).newFileURI
;
68 ////////////////////////////////////////////////////////////////////////////////
69 // string aString: A string of data to be hashed.
70 // string aAlg: optional; the hash algorithm to be used; possible values are: MD2, MD5, SHA1, SHA256, SHA384, and SHA512; defaults to SHA1
71 // string aCharset: optional; the charset used by the passed string; defaults to UTF-8
72 function cryptoHashStr (aString
, aAlg
, aCharset
) {
73 const PR_UINT32_MAX
= 0xffffffff; // this tells updateFromStream to read the entire string
76 let alg
= (""+(aAlg
||"SHA1")).trim().toUpperCase();
77 let charset
= (""+(aCharset
||"UTF-8")).trim();
79 let chashObj
= Cc
["@mozilla.org/security/hash;1"].createInstance(Ci
.nsICryptoHash
);
82 chashObj
.initWithString(alg
);
84 logError("invalid hash algorithm: '"+aAlg
+"'");
85 throw new Error("invalid hash algorithm: '"+aAlg
+"'");
88 let uniconvObj
= Cc
["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci
.nsIScriptableUnicodeConverter
);
90 uniconvObj
.charset
= charset
;
92 logError("invalid charset: '"+aCharset
+"'");
93 throw new Error("invalid charset: '"+aCharset
+"'");
96 if (str
) chashObj
.updateFromStream(uniconvObj
.convertToInputStream(str
), PR_UINT32_MAX
);
97 let hash
= chashObj
.finish(false); // hash as raw octets
98 //return [("0"+hash.charCodeAt(i).toString(16)).slice(-2) for (i in hash)].join("");
100 for (var i
= 0; i
< hash
.length
; ++i
) res
+= ("0"+hash
.charCodeAt(i
).toString(16)).slice(-2);
105 ////////////////////////////////////////////////////////////////////////////////
106 function safeHTMLParser (domWin
, htmlstr
, baseUrl
) {
107 //conlog("domWin: "+domWin);
108 //conlog("htmlstr: "+htmlstr);
109 //conlog("baseUrl: "+baseUrl);
110 let doc
= domWin
.document
.implementation
.createDocument("", "", domWin
.document
.implementation
.createDocumentType("html", "", ""));
111 doc
.appendChild(doc
.createElement("html"));
112 doc
.documentElement
.appendChild(doc
.createElement("body"));
114 let baseUri
= null, frag
;
115 if (typeof(baseUrl
) !== "undefined") baseUri
= uriFromUrl(baseUrl
);
117 let pu
= Cc
["@mozilla.org/parserutils;1"].createInstance(Ci
.nsIParserUtils
);
118 if (!pu
) { logError("FUCKED!"); return null; }
120 frag
= pu
.parseFragment(htmlstr
, 0, false, baseUri
, doc
.body
);
122 doc
.body
.appendChild(frag
);
127 ////////////////////////////////////////////////////////////////////////////////
128 function genUUID () {
129 let uuidgen
= Cc
["@mozilla.org/uuid-generator;1"].createInstance(Ci
.nsIUUIDGenerator
);
130 if (!uuidgen
) throw new Error("no UUID generator available!");
131 return uuidgen
.generateUUID().toString();
135 ////////////////////////////////////////////////////////////////////////////////
136 let storageObjects
= {};
139 function getStorageObject (nfo
) {
140 if (nfo
.name
in storageObjects
) {
141 return storageObjects
[nfo
.name
];
143 let res
= new ScriptStorage(nfo
);
144 storageObjects
[nfo
.name
] = res
;
150 exports
.closeStorageObjects = function () {
151 for (let k
in storageObjects
) {
152 if (typeof(k
) != "string") continue;
153 let dbo
= storageObjects
[k
];
154 //if (typeof(dbo) != "object") continue;
156 debuglog("freeing storage object for '", k
, "'");
164 ////////////////////////////////////////////////////////////////////////////////
165 exports
.createSandbox = function (domWin
, nfo
, url
) {
167 let scres
= nfo
.resources
;
170 // create "unwrapped" sandbox
171 sandbox
= Cu
.Sandbox(domWin
, {
172 sandboxName
: "unwrapped",
173 sameZoneAs
: domWin
, // help GC a little
174 sandboxPrototype
: domWin
/*.wrappedJSObject*/,
177 // alias unsafeWindow for compatibility
178 Cu
.evalInSandbox("const unsafeWindow = window;", sandbox
);
179 //sandbox.isWrapped = !nfo.unwrapped;
181 // create "real" sandbox
182 sandbox
= Cu
.Sandbox(domWin
, {
183 sandboxName
: nfo
.name
,
184 sameZoneAs
: domWin
, // help GC a little
185 sandboxPrototype
: domWin
/*.wrappedJSObject*/,
189 //sandbox.isWrapped = !nfo.unwrapped;
190 // no need for the "bugfix" from GM, as Pale Moon doesn't need it
191 //!sandbox.unsafeWindow = domWin.wrappedJSObject;
192 // alas, Tycho inherited this bug from the new Ff, so reapply it
196 var unsafeWindowGetter = new sandbox.Function("return window.wrappedJSObject||window;");
197 Object.defineProperty(sandbox, "unsafeWindow", {get: unsafeWindowGetter});
205 //sandbox.unsafeWindow = domWin.wrappedJSObject;
206 var unsafeWinObj = domWin.wrappedJSObject;
207 var domwinobj = domWin;
208 var unsafeWindowGetter = function () {
209 logError("unsafeWindowGetter: "+domwinobj);
212 Object.defineProperty(sandbox, "unsafeWindow", {get: unsafeWindowGetter});
213 logError("usfw: "+domWin.wrappedJSObject);
216 logError("XERR: "+e.message);
221 Cu
.evalInSandbox("const unsafeWindow = window.wrappedJSObject||window;", sandbox
, "ECMAv5", "__sandbox_creator__", 1);
223 logError("XERR: "+e
.message
);
226 // functions for interaction with unsafeWindow; see: http://goo.gl/C8Au16
227 sandbox
.createObjectIn
= Cu
.createObjectIn
;
228 sandbox
.cloneInto
= Cu
.cloneInto
;
229 sandbox
.exportFunction
= Cu
.exportFunction
;
231 sandbox
.GM_generateUUID
= tieto(null, genUUID
);
232 sandbox
.GM_cryptoHash
= tieto(null, cryptoHashStr
);
234 //Object.defineProperty(sandbox, "GM_safeHTMLParser", {get: function () tieto(null, safeHTMLParser, domWin)});
235 sandbox
.GM_safeHTMLParser
= tieto(null, safeHTMLParser
, domWin
);
237 let scriptStorage
= getStorageObject(nfo
);
238 sandbox
.GM_getValue
= tieto(scriptStorage
, "getValue");
239 sandbox
.GM_setValue
= tieto(scriptStorage
, "setValue");
240 sandbox
.GM_deleteValue
= tieto(scriptStorage
, "deleteValue");
241 //sandbox.GM_listValues = tieto(scriptStorage, "listValues");
242 sandbox
.GM_listValues
= tieto(null, function (stg
, sbox
) {
243 //scriptStorage, "listValues");
244 let res
= scriptStorage
.listValues();
246 res
= Cu
.cloneInto(res
, sbox
);
249 }, scriptStorage
, sandbox
);
251 sandbox
.GM_xmlhttpRequest
= tieto(new GuerillaXmlHttpReqester(domWin
, url
, sandbox
), "contentStartRequest");
253 sandbox
.GM_addStyle
= tieto(null, function (doc
, cssstr
) {
254 var head
= doc
.getElementsByTagName("head")[0];
256 var style
= doc
.createElement("style");
257 style
.textContent
= cssstr
;
258 style
.type
= "text/css";
259 head
.appendChild(style
);
265 sandbox
.GM_openInTab
= tieto(null, openTab
, domWin
);
267 sandbox
.GM_getResourceText
= tieto(null, function (name
) {
268 if (typeof(name
) === "undefined") throw new Error("GM_getResourceText(): no name given!");
270 let rsrc
= scres
[name
];
271 if (!rsrc
) throw new Error("GM_getResourceText(): no resource found: '"+name
+"'");
272 return fileReadText(rsrc
.file
);
275 sandbox
.GM_getResourceURL
= tieto(null, function (name
) {
276 //logError("GM_getResourceURL(): stub!");
277 //throw new Error("GM_getResourceURL() not implemented");
278 if (typeof(name
) === "undefined") throw new Error("GM_getResourceText(): no name given!");
280 let rsrc
= scres
[name
];
281 if (!rsrc
) throw new Error("GM_getResourceText(): no resource found: '"+name
+"'");
282 let rawdata
= fileReadBinary(rsrc
.file
);
283 return "data:"+rsrc
.contenttype
+";base64,"+encodeURIComponent(btoa(rawdata
));
287 sandbox
.GM_registerMenuCommand
= tieto(null, function () { logError("GM_registerMenuCommand(): stub!"); });
288 sandbox
.GM_setClipboard
= tieto(null, function () { logError("GM_setClipboard(): stub!"); });
291 // provide log functions for both wrapped and unwrapped scripts
292 if (!nfo
.unwrapped
|| nfo
.wantlog
) {
293 sandbox
.conlog
= tieto(null, conlog
);
294 sandbox
.logError
= tieto(null, logError
);
295 if (!nfo
.unwrapped
) sandbox
.GM_log
= tieto(null, conlog
);
298 Object
.defineProperty(sandbox
, "GM_info", {
299 get: tieto(null, function () { logError("GM_info(): stub!"); return {}; }),
302 // nuke sandbox when this window unloaded
303 domWin
.addEventListener("unload", function () {
305 debuglog("**** NUKING SANDBOX *** [", nfo
.name
, "] : [", url
, "]");
306 Cu
.nukeSandbox(sandbox
);
316 ////////////////////////////////////////////////////////////////////////////////
317 exports
.runInSandbox = function (sandbox
, nfo
) {
318 // eval the code, with anonymous wrappers when/if appropriate
319 function evalLazyWrap (code
, fileName
, dowrap
) {
321 Cu
.evalInSandbox(code
, sandbox
, "ECMAv5", fileName
, 1);
323 if (dowrap
&& ("return not in function" == e
.message
)) {
324 // we never anon wrap unless forced to by a "return not in a function" error
325 logError("please, do not use `return` in top-level code in "+fileName
+":"+e
.lineNumber
);
326 Cu
.evalInSandbox("(function(){ "+code
+"\n})()", sandbox
, "ECMAv5", fileName
, 1);
334 // eval the code, with a try/catch to report errors cleanly
335 function evalNoThrow (code
, fileName
, dowrap
) {
338 evalLazyWrap(code
, fileName
);
340 logException("UJS", e
);
351 var dirr
= getUserLibDir();
364 var dirr
= getUserJSDir();
365 dirr
.append(nfo
.name
);
373 let includer = function (fname
) {
374 //debuglog("includer: ", JSON.stringify(fname));
375 if (typeof(fname
) === "object") {
376 if (!("length" in fname
)) throw new Error("string expected");
377 if (fname
.length
== 0) return;
379 if (typeof(fname
) !== "string") throw new Error("string expected");
382 for (let jsfn
of fname
) {
383 if (jsfn
=== undefined || jsfn
=== null) continue;
384 if (typeof(jsfn
) !== "string") throw new Error("string expected");
385 if (jsfn
.length
== 0) continue;
386 if (!(/\.js$/.test(jsfn
))) jsfn
+= ".js";
387 let fl
= buildRelFile(this.dir
, jsfn
);
388 //debuglog("loading ", fl.path);
389 if (!fl
|| !fl
.exists() || fl
.isDirectory() || !fl
.isReadable()) {
390 throw new Error("can't import file: "+jsfn
);
393 let rq
= this.flist
[path
];
394 if (rq
!== undefined) {
395 if (rq
=== false) return; // nothing to do
396 throw new Error("circular import in file: "+jsfn
);
398 this.flist
[path
] = true; // in progress
399 let text
= fileReadText(fl
);
400 evalLazyWrap(text
, newFileURI(fl
).spec
, false);
401 this.flist
[path
] = false; // done
406 sandbox
.requirelib
= tieto(libObj
, includer
);
407 sandbox
.requireinc
= tieto(incObj
, includer
);
410 for (let fl
of nfo
.libs
) {
411 if (!fl
.exists() || fl
.isDirectory() || !fl
.isReadable()) return;
412 if (fl
.path
in reqLibs
) continue; // already required
413 libObj
.flist
[fl
.path
] = true;
414 let text
= fileReadText(fl
);
415 let url
= newFileURI(fl
).spec
;
416 if (!evalNoThrow(text
, url
)) return;
417 libObj
.flist
[fl
.path
] = false;
421 for (let fl
of nfo
.incs
) {
422 if (!fl
.exists() || fl
.isDirectory() || !fl
.isReadable()) return;
423 if (fl
.path
in reqFiles
) continue; // already required
424 incObj
.flist
[fl
.path
] = true;
425 let text
= fileReadText(fl
);
426 let url
= newFileURI(fl
).spec
;
427 if (!evalNoThrow(text
, url
)) return;
428 incObj
.flist
[fl
.path
] = false;
433 if (!nfo
.file
.exists() || nfo
.file
.isDirectory() || !nfo
.file
.isReadable()) return;
434 let text
= fileReadText(nfo
.file
);
435 let url
= newFileURI(nfo
.file
).spec
;
436 evalNoThrow(text
, url
, true);
439 logException("XUJS", e
);