fe-background.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  1. /**
  2. * FE-Helper后台运行程序
  3. * @author [email protected]
  4. */
  5. var BgPageInstance = (function () {
  6. // debug cache,主要记录每个tab的ajax debug 开关
  7. var ajaxDbgCache = {};
  8. //各种元素的就绪情况
  9. var _readyState = {
  10. css: false,
  11. js: false,
  12. html: true,
  13. allDone: false
  14. };
  15. /**
  16. * 文本格式,可以设置一个图标和标题
  17. * @param {Object} options
  18. * @config {string} type notification的类型,可选值:html、text
  19. * @config {string} icon 图标
  20. * @config {string} title 标题
  21. * @config {string} message 内容
  22. */
  23. var notifyText = function (options) {
  24. if (!window.Notification) {
  25. return;
  26. }
  27. if (!options.icon) {
  28. options.icon = "static/img/fe-48.png";
  29. }
  30. if (!options.title) {
  31. options.title = "温馨提示";
  32. }
  33. return chrome.notifications.create('', {
  34. type: 'basic',
  35. title: options.title,
  36. iconUrl: chrome.runtime.getURL(options.icon),
  37. message: options.message
  38. });
  39. };
  40. //侦测就绪情况
  41. var _detectReadyState = function (callback) {
  42. if (_readyState.css && _readyState.js && _readyState.html) {
  43. _readyState.allDone = true;
  44. }
  45. if (_readyState.allDone && typeof callback == 'function') {
  46. callback();
  47. }
  48. };
  49. var _fcp_detect_interval = [];
  50. /**
  51. * 执行前端FCPHelper检测
  52. */
  53. var _doFcpDetect = function (tab) {
  54. //所有元素都准备就绪
  55. if (_readyState.allDone) {
  56. clearInterval(_fcp_detect_interval[tab.id]);
  57. chrome.tabs.sendMessage(tab.id, {
  58. type: MSG_TYPE.BROWSER_CLICKED,
  59. event: MSG_TYPE.FCP_HELPER_DETECT
  60. });
  61. } else if (_fcp_detect_interval[tab.id] === undefined) {
  62. chrome.tabs.sendMessage(tab.id, {
  63. type: MSG_TYPE.BROWSER_CLICKED,
  64. event: MSG_TYPE.FCP_HELPER_INIT
  65. });
  66. //显示桌面提醒
  67. notifyText({
  68. message: "正在准备数据,请稍等..."
  69. });
  70. _fcp_detect_interval[tab.id] = setInterval(function () {
  71. _doFcpDetect(tab);
  72. }, 200);
  73. }
  74. };
  75. /**
  76. * 查看页面wpo信息
  77. */
  78. var _showPageWpoInfo = function (wpoInfo) {
  79. chrome.tabs.query({active: true, currentWindow: true}, function (tabs) {
  80. if (!wpoInfo) {
  81. notifyText({
  82. message: "对不起,检测失败"
  83. });
  84. } else {
  85. chrome.tabs.create({
  86. url: "template/fehelper_wpo.html?" + btoa(encodeURIComponent(JSON.stringify(wpoInfo))),
  87. active: true
  88. });
  89. }
  90. });
  91. };
  92. /**
  93. * 获取页面wpo信息
  94. * @return {[type]}
  95. */
  96. var _getPageWpoInfo = function () {
  97. chrome.tabs.query({active: true, currentWindow: true}, function (tabs) {
  98. var tab = tabs[0];
  99. //显示桌面提醒
  100. chrome.tabs.sendMessage(tab.id, {
  101. type: MSG_TYPE.GET_PAGE_WPO_INFO
  102. });
  103. });
  104. };
  105. /**
  106. * 创建或更新成功执行的动作
  107. * @param evt
  108. * @param content
  109. * @private
  110. */
  111. var _tabUpdatedCallback = function (evt, content) {
  112. return function (newTab) {
  113. if (content) {
  114. setTimeout(function () {
  115. chrome.tabs.sendMessage(newTab.id, {
  116. type: MSG_TYPE.TAB_CREATED_OR_UPDATED,
  117. content: content,
  118. event: evt
  119. });
  120. }, 300)
  121. }
  122. };
  123. };
  124. /**
  125. * 打开对应文件,运行该Helper
  126. * @param tab
  127. * @param file
  128. * @param txt
  129. * @private
  130. */
  131. var _openFileAndRun = function (tab, file, txt) {
  132. chrome.tabs.query({windowId: chrome.windows.WINDOW_ID_CURRENT}, function (tabs) {
  133. var isOpened = false;
  134. var tabId;
  135. var reg = new RegExp("^chrome.*" + file + ".html$", "i");
  136. for (var i = 0, len = tabs.length; i < len; i++) {
  137. if (reg.test(tabs[i].url)) {
  138. isOpened = true;
  139. tabId = tabs[i].id;
  140. break;
  141. }
  142. }
  143. if (!isOpened) {
  144. chrome.tabs.create({
  145. url: 'template/fehelper_' + file + '.html',
  146. active: true
  147. }, _tabUpdatedCallback(file, txt));
  148. } else {
  149. chrome.tabs.update(tabId, {highlighted: true}, _tabUpdatedCallback(file, txt));
  150. }
  151. });
  152. };
  153. /**
  154. * ajax debugger 开关切换
  155. * @private
  156. */
  157. var _debuggerSwitchOn = function () {
  158. chrome.tabs.query({active: true, currentWindow: true}, function (tabs) {
  159. var tab = tabs[0];
  160. ajaxDbgCache[tab.id] = !ajaxDbgCache[tab.id];
  161. chrome.tabs.executeScript(tab.id, {
  162. code: 'console.info("FeHelper提醒:Ajax Debugger开关已' + (ajaxDbgCache[tab.id] ? '开启' : '关闭') + '!");',
  163. allFrames: false
  164. });
  165. });
  166. };
  167. /**
  168. * 告诉DevTools页面,当前的debug开关是否打开
  169. * @param callback
  170. * @private
  171. */
  172. var _tellDevToolsDbgSwitchOn = function (callback) {
  173. chrome.tabs.query({active: true, currentWindow: true}, function (tabs) {
  174. var tab = tabs[0];
  175. callback && callback(ajaxDbgCache[tab.id]);
  176. });
  177. };
  178. /**
  179. * 根据给定参数,运行对应的Helper
  180. */
  181. var _runHelper = function (config) {
  182. chrome.tabs.query({active: true, currentWindow: true}, function (tabs) {
  183. var tab = tabs[0];
  184. // 如果是采用独立文件方式访问,直接打开该页面即可
  185. if (config.useFile == '1') {
  186. var content = config.msgType == MSG_TYPE.QR_CODE ? tab.url : '';
  187. _openFileAndRun(tab, config.msgType, content);
  188. } else {
  189. switch (config.msgType) {
  190. //fcphelper检测
  191. case MSG_TYPE.FCP_HELPER_DETECT:
  192. _doFcpDetect(tab);
  193. break;
  194. //查看网页加载时间
  195. case MSG_TYPE.SHOW_PAGE_LOAD_TIME:
  196. _getPageWpoInfo();
  197. break;
  198. //Ajax调试
  199. case MSG_TYPE.AJAX_DEBUGGER:
  200. _debuggerSwitchOn();
  201. break;
  202. default :
  203. break;
  204. }
  205. }
  206. });
  207. };
  208. /**
  209. * 创建扩展专属的右键菜单
  210. */
  211. var _createContextMenu = function () {
  212. _removeContextMenu();
  213. baidu.contextMenuId = chrome.contextMenus.create({
  214. title: "FeHelper",
  215. contexts: ['page', 'selection', 'editable', 'link', 'image'],
  216. documentUrlPatterns: ['http://*/*', 'https://*/*', 'file://*/*']
  217. });
  218. chrome.contextMenus.create({
  219. title: "二维码生成",
  220. contexts: ['page', 'selection', 'editable', 'link', 'image'],
  221. parentId: baidu.contextMenuId,
  222. onclick: function (info, tab) {
  223. chrome.tabs.executeScript(tab.id, {
  224. code: '(' + (function (pInfo) {
  225. var linkUrl = pInfo.linkUrl;
  226. var pageUrl = pInfo.pageUrl;
  227. var imgUrl = pInfo.srcUrl;
  228. var selection = pInfo.selectionText;
  229. return linkUrl || imgUrl || selection || pageUrl;
  230. }).toString() + ')(' + JSON.stringify(info) + ')',
  231. allFrames: false
  232. }, function (txt) {
  233. _openFileAndRun(tab, 'qrcode', txt);
  234. });
  235. }
  236. });
  237. chrome.contextMenus.create({
  238. type: 'separator',
  239. contexts: ['image'],
  240. parentId: baidu.contextMenuId
  241. });
  242. chrome.contextMenus.create({
  243. title: "二维码解码",
  244. contexts: ['image'],
  245. parentId: baidu.contextMenuId,
  246. onclick: function (info, tab) {
  247. _qrDecode(info, tab);
  248. }
  249. });
  250. chrome.contextMenus.create({
  251. type: 'separator',
  252. contexts: ['all'],
  253. parentId: baidu.contextMenuId
  254. });
  255. chrome.contextMenus.create({
  256. title: "页面取色器",
  257. contexts: ['page', 'selection', 'editable'],
  258. parentId: baidu.contextMenuId,
  259. onclick: function (info, tab) {
  260. _showColorPicker();
  261. }
  262. });
  263. chrome.contextMenus.create({
  264. type: 'separator',
  265. contexts: ['all'],
  266. parentId: baidu.contextMenuId
  267. });
  268. chrome.contextMenus.create({
  269. title: "字符串编解码",
  270. contexts: ['page', 'selection', 'editable'],
  271. parentId: baidu.contextMenuId,
  272. onclick: function (info, tab) {
  273. chrome.tabs.executeScript(tab.id, {
  274. code: '(' + (function () {
  275. return window.getSelection().toString();
  276. }).toString() + ')()',
  277. allFrames: false
  278. }, function (txt) {
  279. _openFileAndRun(tab, 'endecode', txt);
  280. });
  281. }
  282. });
  283. chrome.contextMenus.create({
  284. type: 'separator',
  285. contexts: ['all'],
  286. parentId: baidu.contextMenuId
  287. });
  288. chrome.contextMenus.create({
  289. title: "JSON格式化",
  290. contexts: ['page', 'selection', 'editable'],
  291. parentId: baidu.contextMenuId,
  292. onclick: function (info, tab) {
  293. chrome.tabs.executeScript(tab.id, {
  294. code: '(' + (function () {
  295. return window.getSelection().toString();
  296. }).toString() + ')()',
  297. allFrames: false
  298. }, function (txt) {
  299. _openFileAndRun(tab, 'jsonformat', txt);
  300. });
  301. }
  302. });
  303. chrome.contextMenus.create({
  304. type: 'separator',
  305. contexts: ['all'],
  306. parentId: baidu.contextMenuId
  307. });
  308. chrome.contextMenus.create({
  309. title: "代码格式化",
  310. contexts: ['page', 'selection', 'editable'],
  311. parentId: baidu.contextMenuId,
  312. onclick: function (info, tab) {
  313. chrome.tabs.executeScript(tab.id, {
  314. code: '(' + (function () {
  315. return window.getSelection().toString();
  316. }).toString() + ')()',
  317. allFrames: false
  318. }, function (txt) {
  319. _openFileAndRun(tab, 'codebeautify', txt);
  320. });
  321. }
  322. });
  323. };
  324. /**
  325. * 移除扩展专属的右键菜单
  326. */
  327. var _removeContextMenu = function () {
  328. if (!baidu.contextMenuId) return;
  329. chrome.contextMenus.remove(baidu.contextMenuId);
  330. baidu.contextMenuId = null;
  331. };
  332. /**
  333. * 创建或移除扩展专属的右键菜单
  334. */
  335. var _createOrRemoveContextMenu = function () {
  336. //管理右键菜单
  337. if (baidu.feOption.getOptionItem('opt_item_contextMenus') !== 'false') {
  338. _createContextMenu();
  339. } else {
  340. _removeContextMenu();
  341. }
  342. };
  343. /**
  344. * 二维码转码
  345. * @param info
  346. * @param tab
  347. * @private
  348. */
  349. var _qrDecode = function (info, tab) {
  350. qrcode.callback = function (text) {
  351. if ((text || '').indexOf('error decoding QR Code') !== -1) {
  352. var image = new Image();
  353. image.src = info.srcUrl;
  354. image.onload = function () {
  355. var width = this.naturalWidth;
  356. var height = this.naturalHeight;
  357. // url方式解码失败,再转换成data uri后继续解码
  358. (function createCanvasContext(img, t, l, w, h) {
  359. var canvas = document.createElement('canvas');
  360. canvas.setAttribute('id', 'qr-canvas');
  361. canvas.height = h + 100;
  362. canvas.width = w + 100;
  363. var context = canvas.getContext('2d');
  364. context.fillStyle = 'rgb(255,255,255)';
  365. context.fillRect(0, 0, canvas.width, canvas.height);
  366. context.drawImage(img, l, t, w, h, 50, 50, w, h);
  367. qrcode.callback = function (txt) {
  368. chrome.tabs.sendMessage(tab.id, {
  369. type: MSG_TYPE.QR_DECODE,
  370. result: txt
  371. });
  372. };
  373. qrcode.decode(canvas.toDataURL());
  374. })(image, 0, 0, width, height);
  375. }
  376. } else {
  377. chrome.tabs.sendMessage(tab.id, {
  378. type: MSG_TYPE.QR_DECODE,
  379. result: text
  380. });
  381. }
  382. };
  383. qrcode.decode(info.srcUrl);
  384. };
  385. /**
  386. * 显示color picker
  387. * @private
  388. */
  389. var _showColorPicker = function () {
  390. chrome.tabs.query({active: true, currentWindow: true}, function (tabs) {
  391. var tab = tabs[0];
  392. var tabid = tab.id;
  393. var port = chrome.tabs.connect(tabid, {name: "popupshown"});
  394. chrome.tabs.sendMessage(tabid, {enableColorPicker: true}, function (response) {
  395. chrome.tabs.sendMessage(tabid, {doPick: true}, function (r) {
  396. });
  397. });
  398. });
  399. };
  400. /**
  401. * 将网页截成一张图,实现取色
  402. * @param callback
  403. * @private
  404. */
  405. var _drawColorPicker = function (callback) {
  406. chrome.tabs.query({active: true, currentWindow: true}, function (tabs) {
  407. var tab = tabs[0];
  408. var tabid = tab.id;
  409. chrome.tabs.captureVisibleTab(null, {format: 'png'}, function (dataUrl) {
  410. chrome.tabs.sendMessage(tabid, {
  411. setPickerImage: true,
  412. pickerImage: dataUrl
  413. }, function (response) {
  414. callback && callback();
  415. });
  416. });
  417. });
  418. };
  419. /**
  420. * 在当前页面的控制台输出console
  421. * @param request
  422. * @private
  423. */
  424. var _ajaxDebugger = function (request) {
  425. chrome.tabs.query({active: true, currentWindow: true}, function (tabs) {
  426. var tab = tabs[0];
  427. chrome.tabs.executeScript(tab.id, {
  428. code: "(" + (function (jsonStr) {
  429. var args = JSON.parse(unescape(jsonStr));
  430. console[args[0]].apply(console, Array.prototype.slice.call(args, 1));
  431. }).toString() + ")('" + request.content + "');"
  432. });
  433. });
  434. };
  435. /**
  436. * 接收来自content_scripts发来的消息
  437. */
  438. var _addExtensionListener = function () {
  439. chrome.runtime.onMessage.addListener(function (request, sender, callback) {
  440. //处理CSS的请求
  441. if (request.type == MSG_TYPE.GET_CSS) {
  442. //直接AJAX获取CSS文件内容
  443. baidu.network.readFileContent(request.link, callback);
  444. }
  445. //处理JS的请求
  446. else if (request.type == MSG_TYPE.GET_JS) {
  447. //直接AJAX获取JS文件内容
  448. baidu.network.readFileContent(request.link, callback);
  449. }
  450. //处理HTML的请求
  451. else if (request.type == MSG_TYPE.GET_HTML) {
  452. //直接AJAX获取JS文件内容
  453. baidu.network.readFileContent(request.link, callback);
  454. }
  455. //处理cookie
  456. else if (request.type == MSG_TYPE.GET_COOKIE) {
  457. baidu.network.getCookies(request, callback);
  458. }
  459. //移除cookie
  460. else if (request.type == MSG_TYPE.REMOVE_COOKIE) {
  461. baidu.network.removeCookie(request, callback);
  462. }
  463. //设置cookie
  464. else if (request.type == MSG_TYPE.SET_COOKIE) {
  465. baidu.network.setCookie(request, callback);
  466. }
  467. //CSS准备就绪
  468. else if (request.type == MSG_TYPE.CSS_READY) {
  469. _readyState.css = true;
  470. _detectReadyState(callback);
  471. }
  472. //JS准备就绪
  473. else if (request.type == MSG_TYPE.JS_READY) {
  474. _readyState.js = true;
  475. _detectReadyState(callback);
  476. }
  477. //HTML准备就绪
  478. else if (request.type == MSG_TYPE.HTML_READY) {
  479. _readyState.html = true;
  480. _detectReadyState(callback);
  481. }
  482. //提取配置项
  483. else if (request.type == MSG_TYPE.GET_OPTIONS) {
  484. baidu.feOption.doGetOptions(request.items, callback);
  485. }
  486. //保存配置项
  487. else if (request.type == MSG_TYPE.SET_OPTIONS) {
  488. baidu.feOption.doSetOptions(request.items, callback);
  489. //管理右键菜单
  490. _createOrRemoveContextMenu();
  491. }
  492. //保存当前网页加载时间
  493. else if (request.type == MSG_TYPE.CALC_PAGE_LOAD_TIME) {
  494. _showPageWpoInfo(request.wpo);
  495. }
  496. // 从popup中点过来的
  497. else if (request.type == MSG_TYPE.FROM_POPUP) {
  498. _runHelper(request.config);
  499. }
  500. // color picker
  501. else if (request.type == MSG_TYPE.COLOR_PICKER) {
  502. _drawColorPicker(callback);
  503. }
  504. // console switch
  505. else if (request.type == MSG_TYPE.AJAX_DEBUGGER_SWITCH) {
  506. _tellDevToolsDbgSwitchOn(callback);
  507. }
  508. // console show
  509. else if (request.type == MSG_TYPE.AJAX_DEBUGGER_CONSOLE) {
  510. _ajaxDebugger(request);
  511. }
  512. return true;
  513. });
  514. };
  515. /**
  516. * 初始化
  517. */
  518. var _init = function () {
  519. _addExtensionListener();
  520. _createOrRemoveContextMenu();
  521. };
  522. return {
  523. init: _init,
  524. runHelper: _runHelper,
  525. showColorPicker: _showColorPicker,
  526. tellMeAjaxDbgSwitch: _tellDevToolsDbgSwitchOn
  527. };
  528. })();
  529. //初始化
  530. BgPageInstance.init();