we don't really need window observer
[guerillascript.git] / main / modules / injector.js
blob8654d1b64cfd5a0bf7d06da87fed0201e4711696
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) conlog("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     conlog(s);
82   }
84   // each script is independent (expect unwrapped), so create separate sandboxes
85   let uwpsbox = false;
86   for (let f = 0; f < sclist.length; ++f) {
87     let nfo = sclist[f];
88     if (nfo.unwrapped) {
89       // unwrapped script
90       if (addonOptions.debugMode) {
91         conlog("["+url+"]: unwrapped script '"+nfo.name+"'");
92         //scacheAPI.dumpScriptNfo(nfo);
93       }
94       if (!uwpsbox) uwpsbox = createSandbox(theWin, nfo, url);
95       runInSandbox(uwpsbox, nfo);
96     } else {
97       // wrapped script
98       if (addonOptions.debugMode) {
99         conlog("["+url+"]: wrapped script '"+nfo.name+"'");
100         //scacheAPI.dumpScriptNfo(nfo);
101       }
102       let sbox = createSandbox(theWin, nfo, url);
103       runInSandbox(sbox, nfo);
104     }
105   }
109 ////////////////////////////////////////////////////////////////////////////////
110 function ContentObserver () {}
113 ContentObserver.prototype.QueryInterface = XPCOMUtils.generateQI([Ci.nsIObserver]);
116 ContentObserver.prototype.contentLoad = function (evt) {
117   var contentWin = evt.target.defaultView;
118   // now that we've seen any first load event, stop listening for any more
119   contentWin.removeEventListener("DOMContentLoaded", gContentLoad, true);
120   contentWin.removeEventListener("load", gContentLoad, true);
121   //if (addonOptions.debugMode) conlog("document-end for "+contentWin.document.documentURI);
122   runScripts(contentWin, false);
126 ContentObserver.prototype.observe = function (aSubject, aTopic, aData) {
127   if (!addonOptions.active) return; // we are inactive
128   if (aTopic == "document-element-inserted") {
129     let doc = aSubject;
130     let win = doc && doc.defaultView;
131     if (!doc || !win) return;
132     if (!(doc instanceof Ci.nsIDOMHTMLDocument)) return; //not html document, so its likely an xul document
133     let url = doc.documentURI;
134     if (!scacheAPI.isGoodScheme(url)) return;
135     // listen for whichever kind of load event arrives first
136     win.addEventListener("DOMContentLoaded", gContentLoad, true);
137     win.addEventListener("load", gContentLoad, true);
138     runScripts(win, true);
139   } else {
140     logError("observe: "+aTopic);
141   }
145 ////////////////////////////////////////////////////////////////////////////////
146 var contentObserver = new ContentObserver(); //WARNING: don't change to `let`!
147 var gContentLoad = contentObserver.contentLoad.bind(contentObserver); //WARNING: don't change to `let`!
150 ////////////////////////////////////////////////////////////////////////////////
151 registerStartupHook("injector", function () {
152   Services.obs.addObserver(contentObserver, "document-element-inserted", false);
153   if (!isBootstrapped) openConsole(window);
157 registerShutdownHook("injector", function () {
158   Services.obs.removeObserver(contentObserver, "document-element-inserted");
159   closeStorageObjects();