injected.js 11 KB

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