前阵子没事的时候尝试做了个chrome扩展,主要功能就是用来检测那个微软的邮箱outlook web access的基础版的新邮件的。因为公司现在用的这个邮箱可是这个破邮箱竟然即使有个客户端也不给邮箱提醒功能,估计是硬要别人买个高级版的。
一直刷页面看新邮件麻烦啊,所以起初只是想着找个chrome扩展来刷页面,很容易google到一个刷新工具用着感觉不错,但是用了会发现即使刷新了我也要切到那个tab页才能看到新邮件啊,还是很麻烦那,最终决定还是自己diy下吧。
废话到此开始讲内容吧,chrome扩展的基本开发方式什么的,在 google的 扩展程序开发人员必读 上都有 这边主要就写下我的扩展程序的制作过程吧。
首先建立manifest.json文件,感觉就等于这个扩展的配置文件
{ "name": "OuterLook Checker", //名称 "version": "1.4", //版本 "icons": { "16": "icons/new.png", "32": "icons/no_new.png", "48": "icons/checkIng.png" }, //图标 "description": "OuterLook Checker by Leif", //描述 "background_page": "background.html", //后台运行页面 "browser_action": { "default_title": "OuterLook Checker", //标题 "popup": "popup.html", //点击打开的页面 "default_icon": "icons/no_new.png" //默认图标 }, "options_page": "options.html", //选项页面 "permissions": [ "http://red001.mail.microsoftonline.com/*", "https://red001.mail.microsoftonline.com/*", "tabs" ], //页面过滤这边我只要在邮箱页面运行后台的js "content_scripts": [ { "matches": [https://red001.mail.microsoftonline.com/*], //js脚本添加页面过滤 "js": ["content.js"], //页面添加的js脚本 "run_at":"document_start" //js脚本启动时间 } ] }
然后就是设置页面 options.html ,这边现在只是用来设置下用户名密码而已。
<html> <head> <title>Options for the OuterLook Checker extension</title> </head> <body> <table width="500px;" style="border: thin solid #6699FF; margin: auto; padding:10px;" cellspacing="0" cellpadding="0"> <tr> <td colspan="2"> <h2>User Set</h2> </td> </tr> <tr> <td> User Name: </td> <td> <input type="text" id="txtName" maxlength="50" /> </td> </tr> <tr> <td> Password: </td> <td> <input type="password" id="txtPassword" maxlength="50" /> </td> </tr> <tr> <td colspan="2"> <input type="button" id="btnSave" value="Save" οnclick="saveUser();" /> </td> </tr> </table> <script type="text/javascript"> function saveUser() { //用localStorage保存用户名密码并重置下 localStorage["OWAusername"] = txtName.value; localStorage["OWAPsssward"] = txtPassword.value; localStorage["looptime"] = 0; window.close(); } window.onload = function() { //读取保存的用户名密码 var name = localStorage["OWAusername"] var password = localStorage["OWAPsssward"] if (name != null) { txtName.value = name; } if (password != null) { txtPassword.value = password; } }; </script> </body> </html>
接着是单击显示页面,这个页面实际上大部分代码是chrome扩展 ChromeReload 的
<body οnlοad="restoreOptions()" style="background-color:white;"> <div style="width:10pc;color:black;font-family:sans-serif;font-size:small"> Check mail every: <form name="reload_options"> <!-- Poor man's menu emulation. TODO: highlight when mouse is over --> <!-- XXX Replace the hard-coded indices --> <div style="color:black" width="100%" οnclick="document.reload_options.reloadOption[0].checked = true;rentun false; "> <input type="radio" name="reloadOption" value="0" checked="checked">Never</input> </div> <div style="color:black" width="100%" οnclick="document.reload_options.reloadOption[1].checked = true;rentun false;"> <input type="radio" name="reloadOption" value="5000">5 seconds</input> </div> <div style="color:black" width="100%" οnclick="document.reload_options.reloadOption[2].checked = true;rentun false;"> <input type="radio" name="reloadOption" value="15000">15 seconds</input> </div> <div style="color:black" width="100%" οnclick="document.reload_options.reloadOption[3].checked = true;rentun false;"> <input type="radio" name="reloadOption" value="30000">30 seconds</input> </div> <div style="color:black" width="100%" οnclick="document.reload_options.reloadOption[4].checked = true;rentun false;"> <input type="radio" name="reloadOption" value="60000">1 minute</input> </div> <div style="color:black" width="100%" οnclick="document.reload_options.reloadOption[5].checked = true; rentun false;"> <input type="radio" name="reloadOption" value="120000">2 minutes</input> </div> <div style="color:black" width="100%" οnclick="document.reload_options.reloadOption[6].checked = true; rentun false;"> <input type="radio" name="reloadOption" value="300000">5 minutes</input> </div> <div style="color:black" width="100%" οnclick="document.reload_options.reloadOption[7].checked = true; rentun false;"> <input type="radio" name="reloadOption" value="900000">15 minutes</input> </div> <div style="color:black" width="100%" οnclick="document.reload_options.reloadOption[8].checked = true; rentun false;"> <input type="radio" name="reloadOption" value="1800000">30 minutes</input> </div> <div style="color:black" width="100%" οnclick="document.reload_options.reloadOption[8].checked = true; rentun false;"> <input type="radio" name="reloadOption" value="3600000">1 hour</input> </div> <div style="color:black" width="100%" οnclick="document.reload_options.reloadOption[8].checked = true; rentun false;"> <input type="radio" name="reloadOption" value="7200000">2 hours</input> </div> <input type="submit" value="Set" οnclick="setReloader();"/> <input type="submit" value="DefaultTime Set" οnclick="setAsDefault(); return false;"/> </form> </div> <script> function restoreOptions() { var hasOpened = false; var needOpened = true; //判断如果未打开邮箱页面即自己打开一个(这边传用户名密码没加密,还没有发现一个好的方法来实现这边的加密) chrome.windows.getCurrent(function(wnd) { chrome.tabs.getAllInWindow(wnd.id, function(tabs) { for (var i = 0; i < tabs.length; i++) { hasOpened = tabs[i].url.indexOf("red001.mail.microsoftonline.com") > -1; if (hasOpened) { needOpened = false; } } if (needOpened) { var name = localStorage["OWAusername"]; var password = localStorage["OWAPsssward"]; chrome.tabs.create({ 'url': 'https://red001.mail.microsoftonline.com/owa/auth/logon.aspx?url=https://red001.mail.microsoftonline.com/owa/&reason=0&name=' + name + '&password=' + password }); window.close(); } }); }); var port = chrome.extension.connect({ name: "findReloadTime" }); port.onMessage.addListener(recvData); port.postMessage({ msg: 'getReloadTime' }); } function recvData(data){ if (data.is_random) { document.reload_options.reloadOption[9].checked = true; } else { var reloadTime = data.ms_between_load; for( i = 0; i < document.reload_options.reloadOption.length; i++ ) { if(document.reload_options.reloadOption[i].value == reloadTime) { document.reload_options.reloadOption[i].checked = true; return; } } } } function setReloader() { var option = 0; var isRandom = false; // Walk thru the radios for( i = 0; i < document.reload_options.reloadOption.length; i++ ) { if( document.reload_options.reloadOption[i].checked == true ) { if ('rand' == document.reload_options.reloadOption[i].value) { option = Math.floor(Math.random()*1800000); isRandom = true; } else { option = document.reload_options.reloadOption[i].value; } break; } } var views = chrome.extension.getViews(); for (var i in views) { if (views[i].doReloader) { views[i].doReloader(option, isRandom); } } window.close(); return true; // We use this function in onSubmit; probably not necessary } function setAsDefault() { var option = 0; var isRandom = false; // Walk thru the radios for (i = 0; i < document.reload_options.reloadOption.length; i++) { if (document.reload_options.reloadOption[i].checked == true) { if ('rand' == document.reload_options.reloadOption[i].value) { option = Math.floor(Math.random() * 1800000); isRandom = true; } else { option = document.reload_options.reloadOption[i].value; } break; } } localStorage["DefualtTime"] = option; var views = chrome.extension.getViews(); for (var i in views) { if (views[i].doReloader) { views[i].doReloader(option, isRandom); } } window.close(); return true; } </script> </body>
content.js这段脚本会嵌入指定的页面中即在manifest.json中指定的,所以对于安全性会差点,这里用来自动登录和新邮件提醒的。
window.addEventListener("load", function() { var usernameView = document.getElementById("username"); if (usernameView) { //判断是否需登录 var PasswordView = document.getElementById("password"); var name = getArgs("name"); if (name != null) { usernameView.value = name; var password = getArgs("password"); if (password != null) { PasswordView.value = password; document.cookie = "logondata= 1 &" + name + "; expires=1000000000"; document.forms[0].submit(); //logonForm } } } else { var result = "0"; var classElements = [], allElements = document.getElementsByTagName("td"); for (var i = 0; i < allElements.length; i++) { //在用户在收件箱状态时 判断是否有新邮件(避免草稿箱的误报 ) if ((allElements[i].className == "fld sl bld" || allElements[i].className == "fld bld") && (allElements[i].childNodes[0].title == "收件箱" || allElements[i].childNodes[0].title == "Inbox")) { result = "1"; } } chrome.extension.sendRequest({ emails: result }); } }); function getArgs(strParame) { var args = new Object(); var query = location.search.substring(1); // Get query string var pairs = query.split("&"); // Break at ampersand for (var i = 0; i < pairs.length; i++) { var pos = pairs[i].indexOf('='); // Look for "name=value" if (pos == -1) continue; // If not found, skip var argname = pairs[i].substring(0, pos); // Extract the name var value = pairs[i].substring(pos + 1); // Extract the value value = decodeURIComponent(value); // Decode it, if needed args[argname] = value; // Store as a property } return args[strParame]; // Return the object }
最后就是background.html啦,主要用来接收和响应事件,自动刷新页面部分是ChromeReload的代码啦。
<html> <head> <script type="text/javascript"> var tabs = new Array(); localStorage["looptime"] = "0"; chrome.extension.onConnect.addListener(function(port) { if (port.name === 'findReloadTime') { port.onMessage.addListener(function(data) { if (data.msg === 'getReloadTime') { chrome.tabs.getSelected(null, function(tab) { var tabIsReloaderActive = tabs[tab.id] || false; if (tabIsReloaderActive) { port.postMessage({ ms_between_load: tabs[tab.id].ms_between_load, is_random: tabs[tab.id].is_random }); } }); } }); } }); chrome.extension.onRequest.addListener(function(request, sender, sendResponse) { //接收邮箱发来的信息处理 if (request["emails"] == 1) { chrome.browserAction.setBadgeText({ text: "New" }); chrome.browserAction.setIcon({ path: "icons/new.png" }); if (localStorage["emails"] != "checked") { alert("New Email Received!"); localStorage["emails"] = "checked"; if (localStorage["looptime"] != "0") { doReloader("0", false); } } } else { if (localStorage["looptime"] == undefined || localStorage["looptime"] == "0") { if (localStorage["DefualtTime"] != undefined) { localStorage["looptime"] = localStorage["DefualtTime"]; } else { localStorage["looptime"] = "1800000"; } chrome.browserAction.setBadgeText({ text: "" }); localStorage["emails"] = ""; chrome.browserAction.setIcon({ path: "icons/no_new.png" }); doReloader(localStorage["looptime"], false); } } }); function doReloader(time, isRandom) { if (time > 0) { chrome.tabs.getSelected(null, function(tab) { if (tabs[tab.id] || false) { // if there is already a reloader setup for this tab, cancel it cancelReload(tab.id); } if (tab.title == "收件箱 - Outlook Web Access 基本客户端" || tab.title == "Inbox - Outlook Web Access Light" || tab.title == "收件箱 - Outlook Web Access Light") { tabs[tab.id] = new Array(); tabs[tab.id]['action_url'] = tab.url; tabs[tab.id]['ms_between_load'] = time; tabs[tab.id]['seconds_to_next_reload'] = time / 1000; tabs[tab.id]['is_random'] = isRandom; chrome.tabs.onUpdated.addListener(onUpdateListener); chrome.browserAction.setIcon({ path: "icons/checkIng.png", tabId: tab.id }); localStorage["looptime"] = time; // reload the page // timers are set for the next reload (and a display timer) in the page load listener doReload(tab.id); } }); } else { chrome.tabs.getSelected(null, function(tab) { cancelReload(tab.id); }); } } function onUpdateListener(tabId, changeInfo) { // Hide the badge text chrome.browserAction.setBadgeText({ text: '', tabId: tabId }); var tabIsReloaderActive = (tabs[tabId] || false) && (tabs[tabId].ms_between_load > 0 || false); if (tabIsReloaderActive) { // can only get the URL infomation when the page is loading, not when it is complete if (changeInfo['status'] === 'loading') { // we want to cancel the reloader if they navigate away var urlChanged = changeInfo['url'] || false; if (urlChanged) { // the URL has changed - presumably by navigation // cancel the reloads cancelReload(tabId); } else { // reset the icon (it is cleared when reloaded) chrome.browserAction.setIcon({ path: "icons/checkIng.png", tabId: tabId }); // the icon is reset when the page is reloaded // Cancel the timer display while the tab is reloading cancelDisplayTimer(tabId); // Cancel the reload time (in case the user manually reloaded the page) if (tabs[tabId].reloadTimer || false) { clearTimeout(tabs[tabId].reloadTimer); tabs[tabId].reloadTimer = null; } } } else if (changeInfo['status'] === 'complete') { // page has just completed loading // reset the icon (it is cleared when reloaded) chrome.browserAction.setIcon({ path: "icons/checkIng.png", tabId: tabId }); // the icon is reset when the page is reloaded // if we are using random timouts then we need to reset the time related variables here if (tabs[tabId]['is_random']) { var time = Math.floor(Math.random() * 1800000); tabs[tabId].ms_between_load = time; } // reset the timer for the countdown tabs[tabId].seconds_to_next_reload = tabs[tabId].ms_between_load / 1000; // and set the correct countdown text setBadgeText(tabId); // set a timeout for the next reload setupReloadTimer(tabId); // make really sure this timeout is cancelled before we add a new one cancelDisplayTimer(tabId); // setup the reload countdown tabs[tabId].displayTimer = window.setInterval(function(tab_id) { tabs[tab_id].seconds_to_next_reload--; setBadgeText(tab_id); }, 1000, tabId); } } } function setupReloadTimer(tabId) { // be sure to remove any reload timer before we add a new one if (tabs[tabId].reloadTimer || false) { clearTimeout(tabs[tabId].reloadTimer); tabs[tabId].reloadTimer = null; } // set the reload to occur in ms_between_load milliseconds time tabs[tabId].reloadTimer = window.setTimeout(function(tab_id) { doReload(tab_id); }, tabs[tabId].ms_between_load, tabId); } function cancelDisplayTimer(tabId) { if (tabs[tabId].displayTimer || false) { clearTimeout(tabs[tabId].displayTimer); tabs[tabId].displayTimer = null; } } function setBadgeText(tab_id) { //chrome.browserAction.setBadgeText({text:'p1', tabId:tab_id}); if (tabs[tab_id].seconds_to_next_reload < 0) { // something is wrong! Don't display any text chrome.browserAction.setBadgeText({ text: String(), tabId: tab_id }); // cancel the time which is calling this. It will get recreated if the page is auto-reloaded cancelDisplayTimer(tab_id); } else { var badgeText = String(tabs[tab_id].seconds_to_next_reload); //var mins = Math.floor(tabs[tab_id].seconds_to_next_reload/60); var secs = tabs[tab_id].seconds_to_next_reload % 60; //if (secs < 10) { // secs = '0' + String(secs); //} //badgeText= String(mins) + ':' + String(secs); var mins = Math.floor((tabs[tab_id].seconds_to_next_reload / 60) % 60); var hours = Math.floor((tabs[tab_id].seconds_to_next_reload / (60 * 60)) % 24); var days = Math.floor((tabs[tab_id].seconds_to_next_reload / (60 * 60 * 24))); // Format // The banner fits about 4.5 characters (depending on the character's width) if (days > 999) { badgeText = 'long'; // it will take long... } else if (days > 9) { badgeText = String(days) + 'd'; } else if (days > 0) { // The 'h' will not entirely fit on the banner all the time. Live with it. badgeText = String(days) + 'd' + String(hours) + 'h'; } else if (hours > 0) { if (mins < 10) { mins = '0' + String(mins); } // Blinking ':' // Note: The banner uses a variable-width font; both strings have to have the same width if (secs % 2) { var blinker = ':'; } else { var blinker = ' '; } badgeText = String(hours) + blinker + String(mins); } else { if (secs < 10) { secs = '0' + String(secs); } badgeText = String(mins) + ':' + String(secs); } chrome.browserAction.setBadgeText({ text: badgeText, tabId: tab_id }); } } function cancelReload(tab_id) { if (tabs[tab_id].reloadTimer || false) { clearTimeout(tabs[tab_id].reloadTimer); tabs[tab_id].reloadTimer = null; } if (tabs[tab_id].displayTimer || false) { clearTimeout(tabs[tab_id].displayTimer); tabs[tab_id].displayTimer = null; } tabs[tab_id].ms_between_load = 0; tabs[tab_id].seconds_to_next_reload = 0; chrome.browserAction.setIcon({ path: "icons/no_new.png", tabId: tab_id }); chrome.browserAction.setBadgeText({ text: String(), tabId: tab_id }); localStorage["looptime"] = "0"; } function doReload(tab_id) { chrome.browserAction.setIcon({ path: "icons/checkIng.png", tabId: tab_id }); chrome.browserAction.setBadgeText({ text: '', tabId: tab_id }); // need to work out a way to make POST'ed pages reload chrome.tabs.update(tab_id, { url: tabs[tab_id].action_url }); chrome.tabs.executeScript(tab_id, {code: 'window.location.reload()'}); } </script> </head> </html>
基本上一个简单chrome的扩展也就上面几个文件而已,而且用chrome调试js还是很方便的,所以做过扩展还是很方便的,但是感觉中文的文档还是比较缺的,开始学的时候感觉资源不多啊,在网上找了几个开源的看了下才大概知道了怎么搞。实际上后来我发现要看别人扩展源码很简单啊,直接把那个控制程序下来来解压下就全有了….
哎,第一次写个博客,加上一会东一个事情一会西一个事情,结果整整写了一上午啊,呵呵。我有空还得继续加强下我的扩展啊,下一步目标是学gmail check的邮件提醒方式。