background.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795
  1. function getUniqId() {
  2. return Date.now().toString(36)+Math.random().toString(36).slice(2,6);
  3. }
  4. var db,port=null,pos=0;
  5. function notify(title,options) {
  6. function show() {
  7. var n=new Notification(title+' - '+_('extName'),{
  8. body:options.body,
  9. icon:'images/icon128.png',
  10. });
  11. n.onclick=options.onclick;
  12. }
  13. show();
  14. /*Notification.requestPermission(function(e){
  15. if(e=='granted') show(); else console.log('Notification: '+options.body);
  16. });*/
  17. }
  18. function initDb(callback) {
  19. var request=indexedDB.open('Violentmonkey',1);
  20. request.onsuccess=function(e){db=request.result;if(callback) callback();};
  21. request.onerror=function(e){console.log('IndexedDB error: '+e.target.error.message);};
  22. request.onupgradeneeded=function(e){
  23. var r=e.currentTarget.result,o;
  24. // scripts: id uri custom meta enabled update code position
  25. o=r.createObjectStore('scripts',{keyPath:'id',autoIncrement:true});
  26. o.createIndex('uri','uri',{unique:true});
  27. o.createIndex('update','update',{unique:false});
  28. o.createIndex('position','position',{unique:false}); // should be unique at last
  29. // require: uri code
  30. o=r.createObjectStore('require',{keyPath:'uri'});
  31. // cache: uri data
  32. o=r.createObjectStore('cache',{keyPath:'uri'});
  33. // values: uri values
  34. o=r.createObjectStore('values',{keyPath:'uri'});
  35. };
  36. }
  37. function getNameURI(i) {
  38. var ns=i.meta.namespace||'',n=i.meta.name||'',k=escape(ns)+':'+escape(n)+':';
  39. if(!ns&&!n) k+=i.id;
  40. return k;
  41. }
  42. function isRemote(url){
  43. return url&&!/^data:/.test(url);
  44. }
  45. function getMeta(j){return {id:j.id,custom:j.custom,meta:j.meta,enabled:j.enabled,update:j.update};}
  46. function parseMeta(d){
  47. var o=-1,meta={include:[],exclude:[],match:[],require:[],resource:[],grant:[]};
  48. d.replace(/(?:^|\n)\/\/\s*([@=]\S+)(.*)/g,function(m,k,v){
  49. if(o<0&&k=='==UserScript==') o=1;
  50. else if(k=='==/UserScript==') o=0;
  51. if(o==1&&k[0]=='@') k=k.slice(1); else return;
  52. v=v.replace(/^\s+|\s+$/g,'');
  53. if(meta[k]&&meta[k].push) meta[k].push(v); // multiple values allowed
  54. else if(!(k in meta)) meta[k]=v; // only first value will be stored
  55. });
  56. meta.resources={};
  57. meta.resource.forEach(function(i){
  58. o=i.match(/^(\w\S*)\s+(.*)/);
  59. if(o) meta.resources[o[1]]=o[2];
  60. });
  61. delete meta.resource;
  62. if(!meta.homepageURL&&meta.homepage) meta.homepageURL=meta.homepage; // @homepageURL instead of @homepage
  63. return meta;
  64. }
  65. function newScript() {
  66. var r={
  67. custom: {},
  68. enabled: 1,
  69. update: 1,
  70. code: '// ==UserScript==\n// @name New Script\n// ==/UserScript==\n'
  71. };
  72. r.meta=parseMeta(r.code);
  73. return r;
  74. }
  75. function removeScript(i,src,callback) {
  76. var o=db.transaction('scripts','readwrite').objectStore('scripts');
  77. o.delete(i);
  78. if(callback) callback();
  79. }
  80. function saveScript(i,src,callback) {
  81. var o=db.transaction('scripts','readwrite').objectStore('scripts');
  82. i.enabled=i.enabled?1:0;
  83. i.update=i.update?1:0;
  84. if(!i.position) i.position=++pos;
  85. if(callback) callback();
  86. return o.put(i);
  87. }
  88. function vacuum(o,src,callback) {
  89. var ids=[],cc={},rq={},vl={},w=0,p=0;
  90. function init(){
  91. var o=db.transaction('scripts').objectStore('scripts');
  92. o.index('position').openCursor().onsuccess=function(e){
  93. var r=e.target.result,v,i;
  94. if(r) {
  95. v=r.value;ids.push(v.id);
  96. v.meta.require.forEach(function(i){rq[i]=1;});
  97. for(i in v.meta.resources) cc[v.meta.resources[i]]=1;
  98. if(isRemote(v.meta.icon)) cc[v.meta.icon]=1;
  99. vl[v.uri]=1;
  100. r.continue();
  101. } else vacuumPosition();
  102. };
  103. }
  104. function vacuumPosition(){
  105. var i=ids.shift();
  106. if(i) {
  107. var o=db.transaction('scripts','readwrite').objectStore('scripts');
  108. o.get(i).onsuccess=function(e){
  109. var r=e.target.result;r.position=++p;
  110. o.put(r).onsuccess=vacuumPosition;
  111. };
  112. } else {
  113. pos=p;
  114. vacuumDB('require',rq);
  115. vacuumDB('cache',cc);
  116. vacuumDB('values',vl);
  117. }
  118. }
  119. function vacuumDB(dbName,dic){
  120. w++;
  121. var o=db.transaction(dbName,'readwrite').objectStore(dbName);
  122. o.openCursor().onsuccess=function(e){
  123. var r=e.target.result,v;
  124. if(r) {
  125. v=r.value;
  126. if(!dic[v.uri]) o.delete(v.uri);
  127. else dic[v.uri]++; // stored
  128. r.continue();
  129. } else finish();
  130. };
  131. }
  132. function finish(){
  133. if(!--w) {
  134. var i;
  135. for(i in rq) if(rq[i]==1) fetchRequire(i);
  136. for(i in cc) if(cc[i]==1) fetchCache(i);
  137. chrome.tabs.sendMessage(src.tab.id,{cmd:'Vacuumed'});
  138. }
  139. }
  140. init();
  141. if(callback) callback();
  142. }
  143. function move(data,src,callback){
  144. var o=db.transaction('scripts','readwrite').objectStore('scripts');
  145. o.get(data.id).onsuccess=function(e){
  146. var r=e.target.result,k,s,x=r.position;
  147. if(data.offset<0) {
  148. k=IDBKeyRange.upperBound(x,true);
  149. s='prev';
  150. data.offset=-data.offset;
  151. } else {
  152. k=IDBKeyRange.lowerBound(x,true);
  153. s='next';
  154. }
  155. o.index('position').openCursor(k,s).onsuccess=function(e){
  156. var p=e.target.result,v;
  157. if(p) {
  158. data.offset--;
  159. v=p.value;v.position=x;o.put(v);x=p.key;
  160. if(data.offset) p.continue();
  161. else {r.position=x;o.put(r);}
  162. }
  163. };
  164. };
  165. if(callback) callback();
  166. }
  167. function str2RE(s){return s.replace(/(\.|\?|\/)/g,'\\$1').replace(/\*/g,'.*?');}
  168. function autoReg(s, w) {
  169. if(!w&&s[0]=='/'&&s.slice(-1)=='/') return RegExp(s.slice(1,-1));
  170. return RegExp('^'+str2RE(s)+'$');
  171. }
  172. var match_reg=/(.*?):\/\/([^\/]*)\/(.*)/;
  173. function matchTest(s,u) {
  174. if(s=='<all_urls>') return true;
  175. var m=s.match(match_reg);
  176. if(!m) return false;
  177. // scheme
  178. if(!(
  179. m[1]=='*'&&/^https?$/i.test(u[1]) // * = http|https
  180. ||m[1]==u[1]
  181. )) return false;
  182. // host
  183. if(m[2]!='*') {
  184. if(m[2].slice(0,2)=='*.') {
  185. if(u[2]!=m[2].slice(2)&&u[2].slice(1-m[2].length)!=m[2].slice(1)) return false;
  186. } else if(m[2]!=u[2]) return false;
  187. }
  188. // pathname
  189. if(!autoReg(m[3],1).test(u[3])) return false;
  190. return true;
  191. }
  192. function testURL(url,e) {
  193. var f=true,i,inc=[],exc=[],mat=[],u=url.match(match_reg);
  194. if(e.custom._match!=false&&e.meta.match) mat=mat.concat(e.meta.match);
  195. if(e.custom.match) mat=mat.concat(e.custom.match);
  196. if(e.custom._include!=false&&e.meta.include) inc=inc.concat(e.meta.include);
  197. if(e.custom.include) inc=inc.concat(e.custom.include);
  198. if(e.custom._exclude!=false&&e.meta.exclude) exc=exc.concat(e.meta.exclude);
  199. if(e.custom.exclude) exc=exc.concat(e.custom.exclude);
  200. if(mat.length) {
  201. for(i=0;i<mat.length;i++) if(f=matchTest(mat[i],u)) break;
  202. } else for(i=0;i<inc.length;i++) if(f=autoReg(inc[i]).test(url)) break;
  203. if(f) for(i=0;i<exc.length;i++) if(!(f=!autoReg(exc[i]).test(url))) break;
  204. return f;
  205. }
  206. function getScript(id,src,callback) { // for user edit
  207. var o=db.transaction('scripts').objectStore('scripts');
  208. o.get(id).onsuccess=function(e){
  209. var r=e.target.result,v;
  210. if(r) {
  211. v=getMeta(r);
  212. v.code=r.code;
  213. if(callback) callback(v);
  214. }
  215. };
  216. }
  217. function getMetas(ids,src,callback) { // for popup menu
  218. var o=db.transaction('scripts').objectStore('scripts'),data=[],id;
  219. function getOne(){
  220. var id=ids.shift();
  221. if(id) o.get(id).onsuccess=function(e){
  222. var r=e.target.result;
  223. if(r) data.push(getMeta(r));
  224. getOne();
  225. }; else callback(data);
  226. }
  227. getOne();
  228. }
  229. var badges={};
  230. function setBadge(n,src,callback) {
  231. var o;
  232. if(src.id in badges) o=badges[src.id];
  233. else badges[src.id]=o={num:0};
  234. o.num+=n;
  235. chrome.browserAction.setBadgeBackgroundColor({color:'#808',tabId:src.tab.id});
  236. chrome.browserAction.setBadgeText({text:o.num.toString(),tabId:src.tab.id});
  237. if(o.timer) clearTimeout(o.timer);
  238. o.timer=setTimeout(function(){delete badges[src.id];},300);
  239. callback();
  240. }
  241. function getCacheB64(ids,src,callback) {
  242. var o=db.transaction('cache').objectStore('cache'),data={};
  243. function loop(){
  244. var i=ids.pop();
  245. if(i) o.get(i).onsuccess=function(e){
  246. var r=e.target.result,b,u;
  247. if(r) {
  248. if(typeof r.data=='string') data[i]=r.data;
  249. else
  250. // XXX: old data, update it
  251. setTimeout(function(){fetchCache(i)},1);
  252. }
  253. loop();
  254. }; else callback(data);
  255. }
  256. loop();
  257. }
  258. function getInjected(url,src,callback) { // for injected
  259. function getScripts(){
  260. var o=db.transaction('scripts').objectStore('scripts'),require={};
  261. o.index('position').openCursor().onsuccess=function(e){
  262. var i,r=e.target.result,v;
  263. if(r) {
  264. v=r.value;
  265. if(testURL(url,v)) {
  266. data.scripts.push(v);if(v.enabled) n++;
  267. values.push(v.uri);
  268. v.meta.require.forEach(function(i){require[i]=1;});
  269. for(i in v.meta.resources) cache[v.meta.resources[i]]=1;
  270. }
  271. r.continue();
  272. } else getRequire(Object.getOwnPropertyNames(require));
  273. };
  274. }
  275. function getRequire(require){
  276. function loop(){
  277. var i=require.pop();
  278. if(i) o.get(i).onsuccess=function(e){
  279. var r=e.target.result;
  280. if(r) data.require[i]=r.code;
  281. loop();
  282. }; else getCache();
  283. }
  284. var o=db.transaction('require').objectStore('require');
  285. loop();
  286. }
  287. function getCache(){
  288. getCacheB64(Object.getOwnPropertyNames(cache),src,function(o){
  289. data.cache=o;
  290. getValues();
  291. });
  292. }
  293. function getValues(){
  294. function loop(){
  295. var i=values.pop();
  296. if(i) o.get(i).onsuccess=function(e){
  297. var r=e.target.result;
  298. if(r) data.values[i]=r.values;
  299. loop();
  300. }; else finish();
  301. }
  302. var o=db.transaction('values').objectStore('values');
  303. loop();
  304. }
  305. function finish(){
  306. callback(data);
  307. if(n&&src.url==src.tab.url) chrome.tabs.sendMessage(src.tab.id,{cmd:'GetBadge'});
  308. }
  309. var data={scripts:[],values:{},require:{},injectMode:settings.injectMode},
  310. cache={},values=[],n=0;
  311. if(data.isApplied=settings.isApplied) getScripts(); else finish();
  312. }
  313. function fetchURL(url, cb, type, headers) {
  314. var req=new XMLHttpRequest(),i;
  315. req.open('GET', url, true);
  316. if(type) req.responseType = type;
  317. if(headers) for(i in headers)
  318. req.setRequestHeader(i,headers[i]);
  319. if(cb) req.onloadend = cb;
  320. req.send();
  321. }
  322. var u_cache={},u_require={};
  323. function saveCache(url,data,callback) {
  324. var o=db.transaction('cache','readwrite').objectStore('cache');
  325. o.put({uri:url,data:data}).onsuccess=callback;
  326. }
  327. function fetchCache(url) {
  328. if(u_cache[url]) return;
  329. u_cache[url]=1;
  330. fetchURL(url, function() {
  331. if (this.status!=200) return;
  332. //saveCache(url,this.response,function(){delete u_cache[url];});
  333. var r=new FileReader();
  334. r.onload=function(e){
  335. saveCache(url,window.btoa(r.result),function(){delete u_cache[url];});
  336. };
  337. r.readAsBinaryString(this.response);
  338. }, 'blob');
  339. }
  340. function saveRequire(url,data,callback) {
  341. var o=db.transaction('require','readwrite').objectStore('require');
  342. o.put({uri:url,code:data}).onsuccess=callback;
  343. }
  344. function fetchRequire(url) {
  345. if(u_require[url]) return;
  346. u_require[url]=1;
  347. fetchURL(url, function(){
  348. if(this.status==200) saveRequire(url,this.responseText,function(){delete u_require[url];});
  349. });
  350. }
  351. function updateItem(r){
  352. if(port) try{
  353. port.postMessage(r);
  354. }catch(e){
  355. port=null;
  356. console.log(e);
  357. }
  358. }
  359. function queryScript(id,meta,callback){
  360. var o=db.transaction('scripts').objectStore('scripts');
  361. function finish(r){
  362. if(!r) r=newScript();
  363. if(callback) callback(r);
  364. }
  365. function queryMeta() {
  366. var uri=getNameURI({id:'',meta:meta});
  367. if(uri!='::') o.index('uri').get(uri).onsuccess=function(e){
  368. finish(e.target.result);
  369. }; else finish();
  370. }
  371. function queryId() {
  372. if(id) o.get(id).onsuccess=function(e){
  373. var r=e.target.result;
  374. if(r) finish(r); else queryMeta();
  375. }; else queryMeta();
  376. }
  377. queryId();
  378. }
  379. function parseScript(o,src,callback) {
  380. var i,r={status:0,message:'message' in o?o.message:_('msgUpdated')};
  381. function finish(){
  382. if(src) chrome.tabs.sendMessage(src.tab.id,{cmd:'ShowMessage',data:r});
  383. updateItem(r);
  384. }
  385. if(o.status&&o.status!=200||o.code=='') { // net error
  386. r.status=-1;r.message=_('msgErrorFetchingScript');finish();
  387. } else { // store script
  388. var meta=parseMeta(o.code);
  389. queryScript(o.id,meta,function(c){
  390. if(!c.id){r.status=1;r.message=_('msgInstalled');}
  391. if(o.more) for(i in o.more) if(i in c) c[i]=o.more[i]; // for import and user edit
  392. c.meta=meta;c.code=o.code;c.uri=getNameURI(c);
  393. if(o.from&&!c.meta.homepageURL&&!c.custom.homepageURL&&!/^(file|data):/.test(o.from)) c.custom.homepageURL=o.from;
  394. if(o.url&&!/^(file|data):/.test(o.url)) c.custom.lastInstallURL=o.url;
  395. saveScript(c,src).onsuccess=function(e){
  396. r.id=c.id=e.target.result;r.obj=getMeta(c);finish();
  397. if(!meta.grant.length)
  398. notify(_('Warning'),{
  399. body:_('msgWarnGrant',[meta.name||_('labelNoName')]),
  400. onclick:function(){
  401. chrome.tabs.create({url:'http://wiki.greasespot.net/@grant'});
  402. this.close();
  403. },
  404. });
  405. };
  406. });
  407. meta.require.forEach(function(u){ // @require
  408. var c=o.require&&o.require[u];
  409. if(c) saveRequire(u,c); else fetchRequire(u);
  410. });
  411. for(d in meta.resources) { // @resource
  412. var u=meta.resources[d],c=o.resources&&o.resources[u];
  413. if(c) saveCache(u,c); else fetchCache(u);
  414. }
  415. if(isRemote(meta.icon)) fetchCache(meta.icon); // @icon
  416. }
  417. if(callback) callback();
  418. }
  419. function canUpdate(o,n){
  420. o=(o||'').split('.');
  421. n=(n||'').split('.');
  422. var r=/(\d*)([a-z]*)(\d*)([a-z]*)/i;
  423. while(o.length&&n.length){
  424. var vo=o.shift().match(r),vn=n.shift().match(r);
  425. vo.shift();vn.shift();
  426. vo[0]=parseInt(vo[0]||0,10);
  427. vo[2]=parseInt(vo[2]||0,10);
  428. vn[0]=parseInt(vn[0]||0,10);
  429. vn[2]=parseInt(vn[2]||0,10);
  430. while(vo.length&&vn.length){
  431. var eo=vo.shift(),en=vn.shift();
  432. if(eo!=en) return eo<en;
  433. }
  434. }
  435. return n.length;
  436. }
  437. function setValue(data,src,callback){
  438. var o=db.transaction('values','readwrite').objectStore('values');
  439. o.put({uri:data.uri,values:data.values});
  440. if(callback) callback(); // it seems that CALLBACK does not work with READWRITE transaction
  441. }
  442. function getOption(k,src,callback){
  443. var v=localStorage.getItem(k)||'',r=true;
  444. try{
  445. v=JSON.parse(v);
  446. settings[k]=v;
  447. }catch(e){
  448. v=null;
  449. r=false;
  450. }
  451. if(callback) callback(v);
  452. return r;
  453. }
  454. function setOption(o,src,callback){
  455. if(!o.check||(o.key in settings)) {
  456. localStorage.setItem(o.key,JSON.stringify(o.value));
  457. settings[o.key]=o.value;
  458. }
  459. if(callback) callback(o.value);
  460. }
  461. function initSettings(){
  462. function init(k,v){
  463. if(!getOption(k)) setOption({key:k,value:v});
  464. }
  465. init('isApplied',true);
  466. init('autoUpdate',true);
  467. init('lastUpdate',0);
  468. init('withData',true);
  469. init('closeAfterInstall',false);
  470. init('dataVer',0);
  471. init('injectMode',0);
  472. }
  473. function updateMeta(d,src,callback) {
  474. var o=db.transaction('scripts','readwrite').objectStore('scripts');
  475. o.get(d.id).onsuccess=function(e){
  476. var r=e.target.result,i;
  477. if(!r) return;
  478. for(i in d) if(i in r) r[i]=d[i];
  479. o.put(r).onsuccess=function(e){ // store script without another transaction
  480. updateItem({id:d.id,obj:getMeta(r),status:0});
  481. };
  482. };
  483. if(callback) callback();
  484. }
  485. var _update={};
  486. function checkUpdateO(o) {
  487. if(_update[o.id]) return;_update[o.id]=1;
  488. function finish(){delete _update[o.id];}
  489. var r={id:o.id,updating:1,status:2};
  490. function update() {
  491. if(du) {
  492. r.message=_('msgUpdating');
  493. fetchURL(du,function(){
  494. parseScript({
  495. id: o.id,
  496. status: this.status,
  497. code: this.responseText
  498. });
  499. });
  500. } else r.message='<span class=new>'+_('msgNewVersion')+'</span>';
  501. updateItem(r);finish();
  502. }
  503. var du=o.custom.downloadURL||o.meta.downloadURL||o.custom.lastInstallURL,
  504. u=o.custom.updateURL||o.meta.updateURL||du;
  505. if(u) {
  506. r.message=_('msgCheckingForUpdate');updateItem(r);
  507. fetchURL(u,function() {
  508. r.message=_('msgErrorFetchingUpdateInfo');
  509. if(this.status==200) try {
  510. var m=parseMeta(this.responseText);
  511. if(canUpdate(o.meta.version,m.version)) return update();
  512. r.message=_('msgNoUpdate');
  513. } catch(e){}
  514. delete r.updating;
  515. updateItem(r);finish();
  516. },null,{Accept:'text/x-userscript-meta'});
  517. } else finish();
  518. }
  519. function checkUpdate(id,src,callback) {
  520. var o=db.transaction('scripts').objectStore('scripts');
  521. o.get(id).onsuccess=function(e){
  522. var r=e.target.result;
  523. if(r) checkUpdateO(r);
  524. if(callback) callback();
  525. };
  526. }
  527. function checkUpdateAll(e,src,callback) {
  528. setOption({key:'lastUpdate',value:Date.now()});
  529. var o=db.transaction('scripts').objectStore('scripts');
  530. o.index('update').openCursor(1).onsuccess=function(e){
  531. var r=e.target.result;
  532. if(!r) {
  533. if(callback) callback();
  534. return;
  535. }
  536. checkUpdateO(r.value);
  537. r.continue();
  538. };
  539. }
  540. var checking=false;
  541. function autoCheck() {
  542. function check() {
  543. if(settings.autoUpdate) {
  544. if(Date.now()-settings.lastUpdate>=864e5) checkUpdateAll();
  545. setTimeout(check,36e5);
  546. } else checking=false;
  547. }
  548. if(!checking) {checking=true;check();}
  549. }
  550. function autoUpdate(o,src,callback){
  551. o=!!o;
  552. setOption({key:'autoUpdate',value:o},src,autoCheck);
  553. if(callback) callback(o);
  554. }
  555. function getData(d,src,callback) {
  556. function getScripts(){
  557. var o=db.transaction('scripts').objectStore('scripts');
  558. o.index('position').openCursor().onsuccess=function(e){
  559. var r=e.target.result,v;
  560. if(r) {
  561. v=r.value;
  562. if(isRemote(v.meta.icon)) cache[v.meta.icon]=1;
  563. data.scripts.push(getMeta(v));
  564. r.continue();
  565. } else getCache();
  566. };
  567. function getCache(){
  568. getCacheB64(Object.getOwnPropertyNames(cache),src,function(o){
  569. for(var i in o) o[i]='data:image/png;base64,'+o[i];
  570. data.cache=o;
  571. callback(data);
  572. });
  573. }
  574. }
  575. var data={settings:settings,scripts:[]},cache={};
  576. getScripts();
  577. }
  578. function exportZip(z,src,callback){
  579. function getScripts(){
  580. function loop(){
  581. var i=z.data.shift();
  582. if(i) o.get(i).onsuccess=function(e){
  583. var r=e.target.result;
  584. if(r) {
  585. d.scripts.push(r);
  586. if(z.values) values.push(r.uri);
  587. }
  588. loop();
  589. }; else getValues();
  590. }
  591. var o=db.transaction('scripts').objectStore('scripts');
  592. loop();
  593. }
  594. function getValues(){
  595. function loop(){
  596. var i=values.shift();
  597. if(i) o.get(i).onsuccess=function(e){
  598. var r=e.target.result;
  599. if(r) d.values[i]=r.values;
  600. loop();
  601. }; else finish();
  602. }
  603. if(z.values) {
  604. var o=db.transaction('values').objectStore('values');
  605. d.values={};loop();
  606. } else finish();
  607. }
  608. function finish(){callback(d);}
  609. var d={scripts:[],settings:settings},values=[];
  610. getScripts();
  611. }
  612. // Requests
  613. var requests={},request_id_map={},
  614. special_headers=['user-agent','referer','origin'];
  615. function getRequestId(data,src,callback) {
  616. var id=getUniqId();
  617. // XHR, finalUrl, requestId in browser
  618. requests[id]=[new XMLHttpRequest(),'',''];
  619. callback(id);
  620. }
  621. function httpRequest(details,src,callback) {
  622. function reqCallback(evt) {
  623. function finish(){
  624. chrome.tabs.sendMessage(src.tab.id,{
  625. cmd: 'HttpRequested',
  626. data: {
  627. id: details.id,
  628. type: evt.type,
  629. resType: req.responseType,
  630. data: data
  631. }
  632. });
  633. }
  634. var data={
  635. finalUrl: reqo[1],
  636. readyState: req.readyState,
  637. responseHeaders: req.getAllResponseHeaders(),
  638. status: req.status,
  639. statusText: req.statusText
  640. },r;
  641. try {
  642. data.responseText=req.responseText;
  643. } catch(e) {}
  644. if(req.response&&req.responseType=='blob') {
  645. r=new FileReader();
  646. r.onload=function(e){
  647. data.response=r.result;
  648. finish();
  649. };
  650. r.readAsDataURL(req.response);
  651. } else { // default `null` for blob and '' for text
  652. data.response=req.response;
  653. finish();
  654. }
  655. if(evt.type=='loadend') {
  656. if(reqo[2]) delete request_id_map[reqo[2]];
  657. delete requests[details.id];
  658. }
  659. }
  660. var i,il,v,reqo=requests[details.id],req;
  661. if(!reqo) return;req=reqo[0];
  662. try {
  663. // details.async=true;
  664. req.open(details.method,details.url,true,details.user,details.password);
  665. req.setRequestHeader('VM-Verify',details.id);
  666. if(details.headers)
  667. for(i in details.headers) {
  668. v=details.headers[i];
  669. if(special_headers.indexOf(i.toLowerCase())>=0)
  670. req.setRequestHeader('VM-'+i,v);
  671. else req.setRequestHeader(i,v);
  672. }
  673. if(details.responseType) req.responseType='blob';
  674. if(details.overrideMimeType) req.overrideMimeType(details.overrideMimeType);
  675. ['abort','error','load','loadend','progress','readystatechange','timeout'].forEach(function(i) {
  676. req['on'+i]=reqCallback;
  677. });
  678. reqo[1]=details.url;
  679. req.send(details.data);
  680. } catch(e) {
  681. console.log(e);
  682. }
  683. }
  684. function abortRequest(id) {
  685. var req=requests[id];
  686. if(req) req.abort();
  687. delete requests[id];
  688. }
  689. chrome.runtime.onConnect.addListener(function(p){
  690. port=p;
  691. p.onDisconnect.addListener(function(){port=null;});
  692. });
  693. var settings={};
  694. initSettings();
  695. initDb(function(){
  696. var dataVer=1;
  697. getOption('dataVer',null,function(ver){
  698. pos=null;
  699. var o=db.transaction('scripts','readwrite').objectStore('scripts');
  700. o.index('position').openCursor(null,'prev').onsuccess=function(e){
  701. var r=e.target.result;
  702. if(pos===null) pos=r?r.key:0;
  703. if(ver<dataVer) {
  704. if(r) {
  705. r.value.meta=parseMeta(r.value.code);
  706. o.put(r.value).onsuccess=function(){r.continue();};
  707. } else {
  708. console.log('Data upgraded.');
  709. setOption({key:'dataVer',value:dataVer});
  710. }
  711. }
  712. };
  713. });
  714. chrome.runtime.onMessage.addListener(function(req,src,callback) {
  715. var maps={
  716. NewScript:function(o,src,callback){callback(newScript());},
  717. RemoveScript: removeScript,
  718. GetData: getData,
  719. GetInjected: getInjected,
  720. CheckUpdate: checkUpdate,
  721. CheckUpdateAll: checkUpdateAll,
  722. SaveScript: saveScript,
  723. UpdateMeta: updateMeta,
  724. SetValue: setValue,
  725. GetOption: getOption,
  726. SetOption: setOption,
  727. ExportZip: exportZip,
  728. ParseScript: parseScript,
  729. GetScript: getScript, // for user edit
  730. GetMetas: getMetas, // for popup menu
  731. SetBadge: setBadge,
  732. AutoUpdate: autoUpdate,
  733. Vacuum: vacuum,
  734. Move: move,
  735. ParseMeta: function(o,src,callback){callback(parseMeta(o));},
  736. GetRequestId: getRequestId,
  737. HttpRequest: httpRequest,
  738. AbortRequest: abortRequest,
  739. },f=maps[req.cmd];
  740. if(f) f(req.data,src,callback);
  741. return true;
  742. });
  743. chrome.browserAction.setIcon({path:'images/icon19'+(settings.isApplied?'':'w')+'.png'});
  744. setTimeout(autoCheck,2e4);
  745. });
  746. chrome.webRequest.onBeforeRequest.addListener(function(o){
  747. if(/\.user\.js([\?#]|$)/.test(o.url)) {
  748. var x=new XMLHttpRequest();
  749. x.open('GET',o.url,false);
  750. x.send();
  751. if((!x.status||x.status==200)&&!/^\s*</.test(x.responseText)) {
  752. if(o.tabId<0) chrome.tabs.create({url:chrome.extension.getURL('/confirm.html')+'?url='+encodeURIComponent(o.url)});
  753. else chrome.tabs.get(o.tabId,function(t){
  754. chrome.tabs.create({url:chrome.extension.getURL('/confirm.html')+'?url='+encodeURIComponent(o.url)+'&from='+encodeURIComponent(t.url)});
  755. });
  756. return {redirectUrl:'javascript:history.back()'};
  757. }
  758. }
  759. },{
  760. urls:['<all_urls>'],types:['main_frame']
  761. },['blocking']);
  762. // Modifications on headers
  763. chrome.webRequest.onBeforeSendHeaders.addListener(function(details) {
  764. var headers=details.requestHeaders,new_headers=[],vm_headers={},v,i;
  765. headers.forEach(function(header){
  766. if(header.name.substr(0,3)=='VM-')
  767. vm_headers[header.name.substr(3)]=header.value;
  768. else
  769. new_headers.push(header);
  770. });
  771. v=vm_headers['Verify'];
  772. if(v&&(i=requests[v])) {
  773. delete vm_headers['Verify'];
  774. request_id_map[details.requestId]=v;
  775. i[2]=details.requestId;
  776. for(i in vm_headers)
  777. if(special_headers.indexOf(i.toLowerCase())>=0)
  778. new_headers.push({name:i,value:vm_headers[i]});
  779. }
  780. return {requestHeaders: new_headers};
  781. },{
  782. urls:['<all_urls>'],types: ['xmlhttprequest'],
  783. },["blocking", "requestHeaders"]);
  784. // Watch URL redirects
  785. chrome.webRequest.onBeforeRedirect.addListener(function(details) {
  786. var v=request_id_map[details.requestId],reqo;
  787. if(v) {
  788. reqo=requests[v];
  789. if(reqo) reqo[1]=details.redirectUrl;
  790. }
  791. },{
  792. urls:['<all_urls>'],types: ['xmlhttprequest'],
  793. });