2 * Copyright 2015 Ketmar Dark <ketmar@ketmar.no-ip.org>
3 * Portions copyright 2004-2007 Aaron Boodman
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 uses("resource://gre/modules/XPCOMUtils.jsm");
31 let {closeStorageObjects, createSandbox, runInSandbox} = require("sbapi/sandbox");
33 const stripCredsRE = new RegExp("(://)([^:/]+)(:[^@/]+)?@");
36 ////////////////////////////////////////////////////////////////////////////////
37 function runScripts (theWin, docStart) {
38 if (!addonOptions.active) return; // we are inactive
41 let theDoc = theWin.document;
42 if (!theDoc) return; //document not provided, it is undefined likely
43 if (!theDoc instanceof Ci.nsIDOMHTMLDocument) return; //not html document, so its likely an xul document
46 // When content does (e.g.) history.replacestate() in an inline script,
47 // the location.href changes between document-start and document-end time.
48 // But the content can call replacestate() much later, too. The only way to
49 // be consistent is to ignore it. Luckily, the document.documentURI does
50 // _not_ change, so always use it when deciding whether to run scripts.
51 var url = theDoc.documentURI||"";
52 if (!scacheAPI.isGoodScheme(url)) return;
54 // ignore user/pass in the URL
55 url = url.replace(stripCredsRE, "$1");
57 let sclist = scacheAPI.scriptsForUrl(/*theDoc.location.href*/url, (theWin.frameElement ? true : false), (docStart ? "document-start" : "document-end"));
58 if (addonOptions.debugMode && sclist !== false) debuglog("document-"+(docStart ? "start" : "end")+" for "+url+" ("+(sclist ? sclist.length : 0)+" scripts)");
59 if (!sclist || sclist.length == 0) return;
60 if (addonOptions.debugMode) {
61 let s = "=== "+sclist.length+" scripts found for ["+url+"] ===";
62 for (let f = 0; f < sclist.length; ++f) s += "\n #"+f+": '"+sclist[f].name+"'";
66 // each script is independent (expect unwrapped), so create separate sandboxes
68 for (let nfo of sclist) {
71 if (addonOptions.debugMode) {
72 debuglog("["+url+"]: unwrapped script '"+nfo.name+"'");
73 //scacheAPI.dumpScriptNfo(nfo);
75 if (!uwpsbox) uwpsbox = createSandbox(theWin, nfo, url);
76 runInSandbox(uwpsbox, nfo);
79 if (addonOptions.debugMode) {
80 debuglog("["+url+"]: wrapped script '"+nfo.name+"'");
81 //scacheAPI.dumpScriptNfo(nfo);
83 let sbox = createSandbox(theWin, nfo, url);
84 runInSandbox(sbox, nfo);
90 ////////////////////////////////////////////////////////////////////////////////
91 function ContentObserver () {}
94 ContentObserver.prototype.QueryInterface = XPCOMUtils.generateQI([Ci.nsIObserver]);
97 ContentObserver.prototype.contentLoad = function (evt) {
98 var contentWin = evt.target.defaultView;
99 // now that we've seen any first load event, stop listening for any more
100 contentWin.removeEventListener("DOMContentLoaded", gContentLoad, true);
101 contentWin.removeEventListener("load", gContentLoad, true);
102 //debuglog("document-end for "+contentWin.document.documentURI);
103 runScripts(contentWin, false);
107 ContentObserver.prototype.observe = function (aSubject, aTopic, aData) {
108 if (!addonOptions.active) return; // we are inactive
109 if (aTopic == "document-element-inserted") {
111 let win = doc && doc.defaultView;
112 if (!doc || !win) return;
113 if (!(doc instanceof Ci.nsIDOMHTMLDocument)) return; //not html document, so its likely an xul document
114 // do not try to inject scripts in chrome windows
115 if (win instanceof Ci.nsIDOMChromeWindow) return;
116 let url = doc.documentURI;
117 if (!scacheAPI.isGoodScheme(url)) return;
118 // listen for whichever kind of load event arrives first
119 win.addEventListener("DOMContentLoaded", gContentLoad, true);
120 win.addEventListener("load", gContentLoad, true);
121 runScripts(win, true);
123 logError("observe: "+aTopic);
128 ////////////////////////////////////////////////////////////////////////////////
129 var contentObserver = new ContentObserver(); //WARNING: don't change to `let`!
130 var gContentLoad = contentObserver.contentLoad.bind(contentObserver); //WARNING: don't change to `let`!
133 ////////////////////////////////////////////////////////////////////////////////
134 registerStartupHook("injector", function () {
135 Services.obs.addObserver(contentObserver, "document-element-inserted", false);
139 registerShutdownHook("injector", function () {
140 Services.obs.removeObserver(contentObserver, "document-element-inserted");
141 closeStorageObjects();