we shouldn't init guerilla subsystems before prefs setup
[guerillascript.git] / modules / injector.js
blobbd1d8f492ae4be1d1e1e3450be88841ccbf6293c
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
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
23 * SOFTWARE.
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 let consoleActivated = !guerillaOptions.openConsole;
40 function openConsole (win) {
41 if (consoleActivated) return;
42 consoleActivated = true;
43 let inType = "global:console";
44 let uri = "chrome://global/content/console.xul";
45 let topWindow = Services.wm.getMostRecentWindow(inType);
46 if (topWindow) {
47 //topWindow.focus();
48 } else {
49 win.open(uri, "_blank", "chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar");
50 win.focus();
55 ////////////////////////////////////////////////////////////////////////////////
56 function runScripts (theWin, docStart) {
57 if (!guerillaOptions.active) return; // we are inactive
58 if (!theWin) return;
60 let theDoc = theWin.document;
61 if (!theDoc) return; //document not provided, it is undefined likely
62 if (!theDoc instanceof Ci.nsIDOMHTMLDocument) return; //not html document, so its likely an xul document
65 // When content does (e.g.) history.replacestate() in an inline script,
66 // the location.href changes between document-start and document-end time.
67 // But the content can call replacestate() much later, too. The only way to
68 // be consistent is to ignore it. Luckily, the document.documentURI does
69 // _not_ change, so always use it when deciding whether to run scripts.
70 var url = theWin.document.documentURI;
71 if (!scacheAPI.isGoodScheme(url)) return;
73 // ignore user/pass in the URL
74 url = url.replace(stripCredsRE, "$1");
76 let sclist = scacheAPI.scriptsForUrl(/*theDoc.location.href*/url, (theWin.frameElement ? true : false), (docStart ? "document-start" : "document-end"));
77 if (guerillaOptions.debugMode && sclist !== false) conlog("document-"+(docStart ? "start" : "end")+" for "+url+" ("+(sclist ? sclist.length : 0)+" scripts)");
78 if (!sclist || sclist.length == 0) return;
79 if (guerillaOptions.debugMode) {
80 let s = "=== "+sclist.length+" scripts found for ["+url+"] ===";
81 for (let f = 0; f < sclist.length; ++f) s += "\n #"+f+": '"+sclist[f].name+"'";
82 conlog(s);
85 // each script is independent (expect unwrapped), so create separate sandboxes
86 let uwpsbox = false;
87 for (let f = 0; f < sclist.length; ++f) {
88 let nfo = sclist[f];
89 if (nfo.unwrapped) {
90 // unwrapped script
91 if (guerillaOptions.debugMode) conlog("["+url+"]: unwrapped script '"+nfo.name+"'");
92 if (!uwpsbox) uwpsbox = createSandbox(theWin, nfo, url);
93 runInSandbox(uwpsbox, nfo);
94 } else {
95 // wrapped script
96 if (guerillaOptions.debugMode) conlog("["+url+"]: wrapped script '"+nfo.name+"'");
97 let sbox = createSandbox(theWin, nfo, url);
98 runInSandbox(sbox, nfo);
104 ////////////////////////////////////////////////////////////////////////////////
105 let windowListener = {
106 onOpenWindow: function (aXULWindow) {
107 // wait for the window to finish loading
108 if (consoleActivated) return;
109 let aDOMWindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal||Ci.nsIDOMWindow);
110 aDOMWindow.addEventListener("load", function () {
111 aDOMWindow.removeEventListener("load", arguments.callee, false);
112 windowListener.loadIntoWindow(aDOMWindow, aXULWindow);
113 }, false);
115 onCloseWindow: function (aXULWindow) {},
116 onWindowTitleChange: function (aXULWindow, aNewTitle) {},
117 register: function () {
118 Services.wm.addListener(windowListener);
120 unregister: function () { Services.wm.removeListener(windowListener); },
121 loadIntoWindow: function (aDOMWindow, aXULWindow) {
122 if (!aDOMWindow || !aDOMWindow.gBrowser) return;
123 openConsole(aDOMWindow);
125 unloadFromWindow: function (aDOMWindow, aXULWindow) {
130 ////////////////////////////////////////////////////////////////////////////////
131 function ContentObserver () {}
134 ////////////////////////////////////////////////////////////////////////////////
135 ContentObserver.prototype.QueryInterface = XPCOMUtils.generateQI([Ci.nsIObserver]);
138 ContentObserver.prototype.contentLoad = function (evt) {
139 var contentWin = evt.target.defaultView;
140 // now that we've seen any first load event, stop listening for any more
141 contentWin.removeEventListener("DOMContentLoaded", gContentLoad, true);
142 contentWin.removeEventListener("load", gContentLoad, true);
143 //if (guerillaOptions.debugMode) conlog("document-end for "+contentWin.document.documentURI);
144 runScripts(contentWin, false);
148 ContentObserver.prototype.observe = function (aSubject, aTopic, aData) {
149 if (!guerillaOptions.active) return; // we are inactive
150 if (aTopic == "document-element-inserted") {
151 let doc = aSubject;
152 let win = doc && doc.defaultView;
153 if (!doc || !win) return;
154 if (!(doc instanceof Ci.nsIDOMHTMLDocument)) return; //not html document, so its likely an xul document
155 let url = doc.documentURI;
156 if (!scacheAPI.isGoodScheme(url)) return;
157 // listen for whichever kind of load event arrives first
158 win.addEventListener("DOMContentLoaded", gContentLoad, true);
159 win.addEventListener("load", gContentLoad, true);
161 win.addEventListener("popstate", function (evt) {
162 logError("POPSTATE!\n"+
163 " state: "+JSON.stringify(evt.state)+"\n"+
164 " location: "+doc.location);
165 }, true);
166 win.addEventListener("pagehide", function (evt) {
167 logError("PAGEHIDE!\n"+
168 " location: "+doc.location);
169 }, true);
170 win.addEventListener("pageshow", function (evt) {
171 logError("PAGESHOW!\n"+
172 " location: "+doc.location);
173 }, true);
174 win.addEventListener("unload", function (evt) {
175 logError("UNLOAD!\n"+
176 " location: "+doc.location);
177 }, true);
178 win.addEventListener("readystatechange", function (evt) {
179 logError("STATE!\n"+
180 " state: "+doc.readyState+"\n"+
181 " location: "+doc.location);
182 }, true);
184 //if (guerillaOptions.debugMode) conlog("document-start for "+doc.documentURI);
185 runScripts(win, true);
186 } else {
187 logError("observe: "+aTopic);
192 ////////////////////////////////////////////////////////////////////////////////
193 var contentObserver = new ContentObserver(); //WARNING: don't change to `let`!
194 var gContentLoad = contentObserver.contentLoad.bind(contentObserver); //WARNING: don't change to `let`!
197 ////////////////////////////////////////////////////////////////////////////////
198 exports.wlRegister = function () {
199 windowListener.register();
200 Services.obs.addObserver(contentObserver, "document-element-inserted", false);
203 exports.wlUnregister = function () {
204 windowListener.unregister();
205 Services.obs.removeObserver(contentObserver, "document-element-inserted");
208 exports.deinitInjectorStorage = function () {
209 closeStorageObjects();