injected.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. /**
  2. * http://www.webtoolkit.info/javascript-utf8.html
  3. */
  4. function utf8decode (utftext) {
  5. var string = "";
  6. var i = 0;
  7. var c = 0, c1 = 0, c2 = 0, c3 = 0;
  8. while ( i < utftext.length ) {
  9. c = utftext.charCodeAt(i);
  10. if (c < 128) {string += String.fromCharCode(c);i++;}
  11. else if((c > 191) && (c < 224)) {
  12. c2 = utftext.charCodeAt(i+1);
  13. string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
  14. i += 2;
  15. } else {
  16. c2 = utftext.charCodeAt(i+1);
  17. c3 = utftext.charCodeAt(i+2);
  18. string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
  19. i += 3;
  20. }
  21. }
  22. return string;
  23. }
  24. // Messages
  25. var id=Math.random();
  26. function post(data,callback){
  27. if(!data.data) data.data={};
  28. data.data['frameid']=id;
  29. chrome.runtime.sendMessage(data);
  30. }
  31. chrome.runtime.onMessage.addListener(function(req,src) {
  32. if(req.frameid&&req.frameid!=id) return;
  33. var maps={
  34. Command:command,
  35. GetPopup: getPopup,
  36. },f=maps[req.cmd];
  37. if(f) f(req.data,src);
  38. });
  39. function getPopup(){
  40. chrome.runtime.sendMessage({cmd:'SetPopup',data:[menu,ids]});
  41. }
  42. // Communicator
  43. var comm={
  44. vmid:'VM'+Math.random(),
  45. sid:null,
  46. did:null,
  47. init:function(s,d){
  48. comm.sid=comm.vmid+s;
  49. comm.did=comm.vmid+d;
  50. document.addEventListener(comm.sid,comm['handle'+s],false);
  51. },
  52. post:function(d){
  53. var e=document.createEvent("MutationEvent");
  54. e.initMutationEvent(comm.did,false,false,null,null,null,JSON.stringify(d),e.ADDITION);
  55. document.dispatchEvent(e);
  56. },
  57. handleR:function(e){
  58. var o=JSON.parse(e.attrName),maps={
  59. LoadScript:comm.loadScript,
  60. Command:function(o){
  61. var f=comm.command[o];
  62. if(f) f();
  63. },
  64. GotRequestId:function(o){comm.qrequests.shift().start(o);},
  65. HttpRequested:function(o){
  66. var c=comm.requests[o.id];
  67. if(c) c.callback(o);
  68. },
  69. },f=maps[o.cmd];
  70. if(f) f(o.data);
  71. },
  72. loadScript:function(o){
  73. var start=[],body=[],end=[],cache,require,values,elements;
  74. comm.command={};comm.requests={};comm.qrequests=[];
  75. function wrapper(c){
  76. var t=this,value=values[c.uri];if(!value) value={};
  77. // functions and properties
  78. function wrapFunction(o,i,c){
  79. var f=function(){
  80. var r;
  81. try{r=Function.apply.apply(o[i],[o,arguments]);}
  82. catch(e){console.log('Error calling '+i+': \n'+e.stack);}
  83. if(c) r=c(r);return r;
  84. };
  85. f.__proto__=o[i];f.prototype=o[i].prototype;
  86. return f;
  87. }
  88. function wrapWindow(w){return w==window?t:w;}
  89. function wrapItem(i){
  90. try{ // avoid reading protected data
  91. if(typeof window[i]=='function') {
  92. if(itemWrapper) t[i]=itemWrapper(window,i,wrapWindow);
  93. else t[i]=window[i];
  94. } else Object.defineProperty(t,i,{
  95. get:function(){return wrapWindow(window[i]);},
  96. set:function(v){window[i]=v;},
  97. });
  98. }catch(e){}
  99. }
  100. var itemWrapper=null;
  101. Object.getOwnPropertyNames(window).forEach(wrapItem);
  102. itemWrapper=wrapFunction;
  103. n=window;while(n=Object.getPrototypeOf(n)) Object.getOwnPropertyNames(n).forEach(wrapItem);
  104. function getCache(name){for(var i in resources) if(name==i) return cache[resources[i]];}
  105. function propertyToString(){return 'Property for Violentmonkey: designed by Gerald';}
  106. function addProperty(name,prop,obj){
  107. if('value' in prop) prop.writable=false;
  108. prop.configurable=false;
  109. if(!obj) {obj=t;elements.push(name);}
  110. Object.defineProperty(obj,name,prop);
  111. if(typeof obj[name]=='function') obj[name].toString=propertyToString;
  112. }
  113. var resources=c.meta.resources||{};elements=[];
  114. addProperty('unsafeWindow',{value:window});
  115. // GM functions
  116. // Reference: http://wiki.greasespot.net/Greasemonkey_Manual:API
  117. addProperty('GM_info',{get:function(){
  118. var m=c.code.match(/\/\/\s+==UserScript==\s+([\s\S]*?)\/\/\s+==\/UserScript==\s/),
  119. script={
  120. description:c.meta.description||'',
  121. excludes:c.meta.exclude.concat(),
  122. includes:c.meta.include.concat(),
  123. matches:c.meta.match.concat(),
  124. name:c.meta.name||'',
  125. namespace:c.meta.namespace||'',
  126. resources:{},
  127. 'run-at':c.meta['run-at']||'document-end',
  128. unwrap:false,
  129. version:c.meta.version||'',
  130. },
  131. o={};
  132. addProperty('script',{value:{}},o);
  133. addProperty('scriptMetaStr',{value:m?m[1]:''},o);
  134. addProperty('scriptWillUpdate',{value:c.update},o);
  135. addProperty('version',{value:undefined},o);
  136. for(m in script) addProperty(m,{value:script[m]},o.script);
  137. for(m in c.meta.resources) addProperty(m,{value:c.meta.resources[m]},o.script.resources);
  138. return o;
  139. }});
  140. addProperty('GM_deleteValue',{value:function(key){delete value[key];comm.post({cmd:'SetValue',data:{uri:c.uri,values:value}});}});
  141. addProperty('GM_getValue',{value:function(k,d){
  142. var v=value[k];
  143. if(v) {
  144. k=v[0];
  145. v=v.slice(1);
  146. switch(k){
  147. case 'n': d=Number(v);break;
  148. case 'b': d=v=='true';break;
  149. case 'o': try{d=JSON.parse(v);}catch(e){console.log(e);}break;
  150. default: d=v;
  151. }
  152. }
  153. return d;
  154. }});
  155. addProperty('GM_listValues',{value:function(){return Object.getOwnPropertyNames(value);}});
  156. addProperty('GM_setValue',{value:function(key,val){
  157. var t=(typeof val)[0];
  158. switch(t){
  159. case 'o':val=t+JSON.stringify(val);break;
  160. default:val=t+val;
  161. }
  162. value[key]=val;comm.post({cmd:'SetValue',data:{uri:c.uri,values:value}});
  163. }});
  164. addProperty('GM_getResourceText',{value:function(name){
  165. var b=getCache(name);
  166. if(b) b=utf8decode(b);
  167. return b;
  168. }});
  169. addProperty('GM_getResourceURL',{value:function(name){
  170. return getCache(name);
  171. }});
  172. addProperty('GM_addStyle',{value:function(css){
  173. if(!document.head) return;
  174. var v=document.createElement('style');
  175. v.innerHTML=css;
  176. document.head.appendChild(v);
  177. return v;
  178. }});
  179. addProperty('GM_log',{value:console.log});
  180. addProperty('GM_openInTab',{value:function(url){window.open(url);}});
  181. addProperty('GM_registerMenuCommand',{value:function(cap,func,acc){
  182. comm.command[cap]=func;comm.post({cmd:'RegisterMenu',data:[cap,acc]});
  183. }});
  184. function Request(details){
  185. this.callback=function(d){
  186. var c=details['on'+d.type];
  187. if(c) c(d.data);
  188. if(!this.id) for(var i in d.data) this.req[i]=d.data[i];
  189. if(d.type=='load') delete comm.requests[this.id];
  190. };
  191. this.start=function(id){
  192. this.id=id;
  193. comm.requests[id]=this;
  194. comm.post({cmd:'HttpRequest',data:{
  195. id:id,
  196. method:details.method,
  197. url:details.url,
  198. data:details.data,
  199. async:!details.synchronous,
  200. user:details.user,
  201. password:details.password,
  202. headers:details.headers,
  203. overrideMimeType:details.overrideMimeType,
  204. }});
  205. };
  206. this.req={
  207. abort:function(){comm.post({cmd:'AbortRequest',data:this.id});}
  208. };
  209. comm.qrequests.push(this);
  210. comm.post({cmd:'GetRequestId'});
  211. };
  212. addProperty('GM_xmlhttpRequest',{value:function(details){
  213. var r=new Request(details);
  214. return r.req;
  215. }});
  216. }
  217. function runStart(){while(start.length) runCode(start.shift());}
  218. function runBody(){
  219. if(document.body) {
  220. window.removeEventListener('DOMNodeInserted',runBody,true);
  221. while(body.length) runCode(body.shift());
  222. }
  223. }
  224. function runEnd(){while(end.length) runCode(end.shift());}
  225. function runCode(c){
  226. var w=new wrapper(c),req=c.meta.require||[],i,r,f,code=[];
  227. elements.forEach(function(i){code.push(i+'=window.'+i);});
  228. code=['(function(){var '+code.join(',')+';'];
  229. for(i=0;i<req.length;i++) if(r=require[req[i]]) code.push(r);
  230. code.push(c.code);code.push('})();');
  231. code=code.join('\n');
  232. f=new Function('w','with(w) eval('+JSON.stringify(code)+');');
  233. try{f.call(w,w);}catch(e){
  234. console.log('Error running script: '+(c.custom.name||c.meta.name||c.id)+'\n'+e);
  235. }
  236. }
  237. var l;
  238. o.scripts.forEach(function(i){
  239. if(i&&i.enabled) {
  240. switch(i.custom['run-at']||i.meta['run-at']){
  241. case 'document-start': l=start;break;
  242. case 'document-body': l=body;break;
  243. default: l=end;
  244. }
  245. l.push(i);
  246. }
  247. });
  248. require=o.require;
  249. cache=o.cache;
  250. values=o.values;
  251. runStart();
  252. window.addEventListener('DOMNodeInserted',runBody,true);
  253. window.addEventListener('DOMContentLoaded',runEnd,false);
  254. runBody();
  255. if(document.readyState=='complete') runEnd();
  256. },
  257. },menu=[],ids=[];
  258. function handleC(e){
  259. var o=JSON.parse(e.attrName),maps={
  260. SetValue:function(o){post({cmd:'SetValue',data:o});},
  261. RegisterMenu:menu.push.bind(menu),
  262. GetRequestId:getRequestId,
  263. HttpRequest:httpRequest,
  264. AbortRequest:abortRequest,
  265. },f=maps[o.cmd];
  266. if(f) f(o.data);
  267. }
  268. function command(o){
  269. comm.post({cmd:'Command',data:o});
  270. }
  271. // Requests
  272. var requests={};
  273. function getRequestId() {
  274. var id=Date.now()+Math.random().toString().slice(1);
  275. requests[id]=new XMLHttpRequest();
  276. comm.post({cmd:'GotRequestId',data:id});
  277. }
  278. function httpRequest(details) {
  279. function callback(evt) {
  280. comm.post({
  281. cmd: 'HttpRequested',
  282. data: {
  283. id: details.id,
  284. type: evt.type,
  285. data: {
  286. readyState: req.readyState,
  287. responseHeaders: req.getAllResponseHeaders(),
  288. responseText: req.responseText,
  289. status: req.status,
  290. statusText: req.statusText
  291. }
  292. }
  293. });
  294. }
  295. var i,req;
  296. if(details.id) req=requests[details.id]; else req=new XMLHttpRequest();
  297. try {
  298. req.open(details.method,details.url,details.async,details.user,details.password);
  299. if(details.headers) for(i in details.headers) req.setRequestHeader(i,details.headers[i]);
  300. if(details.overrideMimeType) req.overrideMimeType(details.overrideMimeType);
  301. ['abort','error','load','progress','readystatechange','timeout'].forEach(function(i) {
  302. req['on'+i]=callback;
  303. });
  304. req.send(details.data);
  305. if(!details.id) callback({type:'load'});
  306. } catch (e) {
  307. console.log(e);
  308. }
  309. }
  310. function abortRequest(id) {
  311. var req=requests[id];
  312. if(req) req.abort();
  313. delete requests[id];
  314. }
  315. // For injected scripts
  316. function objEncode(o){
  317. var t=[],i;
  318. for(i in o) {
  319. if(!o.hasOwnProperty(i)) continue;
  320. if(typeof o[i]=='function') t.push(i+':'+o[i].toString());
  321. else t.push(i+':'+JSON.stringify(o[i]));
  322. }
  323. return '{'+t.join(',')+'}';
  324. }
  325. function initCommunicator(){
  326. var s=document.createElement('script'),d=document.documentElement,C='C',R='R';
  327. s.innerHTML='(function(){var comm='+objEncode(comm)+';comm.init("'+R+'","'+C+'");})();';
  328. d.appendChild(s);d.removeChild(s);
  329. comm.handleC=handleC;comm.init(C,R);
  330. chrome.runtime.sendMessage({cmd:'FindScript'},loadScript);
  331. }
  332. function loadScript(o){
  333. o.scripts.forEach(function(i){ids.push(i.id);});
  334. comm.post({cmd:'LoadScript',data:o});
  335. }
  336. initCommunicator();