carcass fixes
[guerillascript.git] / main / modules / injector.js
blob802c29372640e460718b401553a556504b5d85d3
1 /*
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
5  *
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:
12  *
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.
16  *
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
23  * SOFTWARE.
24  *
25  * The above copyright notice and this permission notice shall be included in all
26  * copies or substantial portions of the Software.
27  */
28 ////////////////////////////////////////////////////////////////////////////////
29 uses("resource://gre/modules/XPCOMUtils.jsm");
31 let {closeStorageObjects, createSandbox, runInSandbox} = require("sbapi/sandbox");
33 const stripCredsRE = new RegExp("(://)([^:/]+)(:[^@/]+)?@");
36 ////////////////////////////////////////////////////////////////////////////////
37 let consoleActivated = !addonOptions.openConsole;
39 function openConsole (win) {
40   if (consoleActivated) return;
41   consoleActivated = true;
42   let inType = "global:console";
43   let uri = "chrome://global/content/console.xul";
44   let topWindow = Services.wm.getMostRecentWindow(inType);
45   if (topWindow) {
46     //topWindow.focus();
47   } else {
48     win.open(uri, "_blank", "chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar");
49     win.focus();
50   }
54 ////////////////////////////////////////////////////////////////////////////////
55 function runScripts (theWin, docStart) {
56   if (!addonOptions.active) return; // we are inactive
57   if (!theWin) return;
59   let theDoc = theWin.document;
60   if (!theDoc) return; //document not provided, it is undefined likely
61   if (!theDoc instanceof Ci.nsIDOMHTMLDocument) return; //not html document, so its likely an xul document
64   // When content does (e.g.) history.replacestate() in an inline script,
65   // the location.href changes between document-start and document-end time.
66   // But the content can call replacestate() much later, too.  The only way to
67   // be consistent is to ignore it.  Luckily, the  document.documentURI does
68   // _not_ change, so always use it when deciding whether to run scripts.
69   var url = theWin.document.documentURI;
70   if (!scacheAPI.isGoodScheme(url)) return;
72   // ignore user/pass in the URL
73   url = url.replace(stripCredsRE, "$1");
75   let sclist = scacheAPI.scriptsForUrl(/*theDoc.location.href*/url, (theWin.frameElement ? true : false), (docStart ? "document-start" : "document-end"));
76   if (addonOptions.debugMode && sclist !== false) debuglog("document-"+(docStart ? "start" : "end")+" for "+url+" ("+(sclist ? sclist.length : 0)+" scripts)");
77   if (!sclist || sclist.length == 0) return;
78   if (addonOptions.debugMode) {
79     let s = "=== "+sclist.length+" scripts found for ["+url+"] ===";
80     for (let f = 0; f < sclist.length; ++f) s += "\n  #"+f+": '"+sclist[f].name+"'";
81     debuglog(s);
82   }
84   // each script is independent (expect unwrapped), so create separate sandboxes
85   let uwpsbox = false;
86   for (let nfo of sclist) {
87     if (nfo.unwrapped) {
88       // unwrapped script
89       if (addonOptions.debugMode) {
90         debuglog("["+url+"]: unwrapped script '"+nfo.name+"'");
91         //scacheAPI.dumpScriptNfo(nfo);
92       }
93       if (!uwpsbox) uwpsbox = createSandbox(theWin, nfo, url);
94       runInSandbox(uwpsbox, nfo);
95     } else {
96       // wrapped script
97       if (addonOptions.debugMode) {
98         debuglog("["+url+"]: wrapped script '"+nfo.name+"'");
99         //scacheAPI.dumpScriptNfo(nfo);
100       }
101       let sbox = createSandbox(theWin, nfo, url);
102       runInSandbox(sbox, nfo);
103     }
104   }
108 ////////////////////////////////////////////////////////////////////////////////
109 function ContentObserver () {}
112 ContentObserver.prototype.QueryInterface = XPCOMUtils.generateQI([Ci.nsIObserver]);
115 ContentObserver.prototype.contentLoad = function (evt) {
116   var contentWin = evt.target.defaultView;
117   // now that we've seen any first load event, stop listening for any more
118   contentWin.removeEventListener("DOMContentLoaded", gContentLoad, true);
119   contentWin.removeEventListener("load", gContentLoad, true);
120   //debuglog("document-end for "+contentWin.document.documentURI);
121   runScripts(contentWin, false);
125 ContentObserver.prototype.observe = function (aSubject, aTopic, aData) {
126   if (!addonOptions.active) return; // we are inactive
127   if (aTopic == "document-element-inserted") {
128     let doc = aSubject;
129     let win = doc && doc.defaultView;
130     if (!doc || !win) return;
131     if (!(doc instanceof Ci.nsIDOMHTMLDocument)) return; //not html document, so its likely an xul document
132     let url = doc.documentURI;
133     if (!scacheAPI.isGoodScheme(url)) return;
134     // listen for whichever kind of load event arrives first
135     win.addEventListener("DOMContentLoaded", gContentLoad, true);
136     win.addEventListener("load", gContentLoad, true);
137     runScripts(win, true);
138   } else {
139     logError("observe: "+aTopic);
140   }
144 ////////////////////////////////////////////////////////////////////////////////
145 var contentObserver = new ContentObserver(); //WARNING: don't change to `let`!
146 var gContentLoad = contentObserver.contentLoad.bind(contentObserver); //WARNING: don't change to `let`!
149 ////////////////////////////////////////////////////////////////////////////////
150 registerStartupHook("injector", function () {
151   Services.obs.addObserver(contentObserver, "document-element-inserted", false);
152   if (!isBootstrapped) openConsole(window);
156 registerShutdownHook("injector", function () {
157   Services.obs.removeObserver(contentObserver, "document-element-inserted");
158   closeStorageObjects();