Show badge with the number of matching entries (#50)
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
require("chrome-extension-async");
|
require("chrome-extension-async");
|
||||||
|
var TldJS = require("tldjs");
|
||||||
var sha1 = require("sha1");
|
var sha1 = require("sha1");
|
||||||
|
|
||||||
// native application id
|
// native application id
|
||||||
@@ -17,8 +18,12 @@ var defaultSettings = {
|
|||||||
|
|
||||||
var authListeners = {};
|
var authListeners = {};
|
||||||
|
|
||||||
|
chrome.browserAction.setBadgeBackgroundColor({
|
||||||
|
color: "#666"
|
||||||
|
});
|
||||||
|
|
||||||
// watch for tab updates
|
// watch for tab updates
|
||||||
chrome.tabs.onUpdated.addListener(function(tabId, info) {
|
chrome.tabs.onUpdated.addListener((tabId, info) => {
|
||||||
// ignore non-complete status
|
// ignore non-complete status
|
||||||
if (info.status !== "complete") {
|
if (info.status !== "complete") {
|
||||||
return;
|
return;
|
||||||
@@ -29,6 +34,9 @@ chrome.tabs.onUpdated.addListener(function(tabId, info) {
|
|||||||
chrome.webRequest.onAuthRequired.removeListener(authListeners[tabId]);
|
chrome.webRequest.onAuthRequired.removeListener(authListeners[tabId]);
|
||||||
delete authListeners[tabId];
|
delete authListeners[tabId];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// show number of matching passwords in a badge
|
||||||
|
updateMatchingPasswordsCount(tabId);
|
||||||
});
|
});
|
||||||
|
|
||||||
// handle incoming messages
|
// handle incoming messages
|
||||||
@@ -43,6 +51,79 @@ chrome.runtime.onInstalled.addListener(onExtensionInstalled);
|
|||||||
|
|
||||||
//----------------------------------- Function definitions ----------------------------------//
|
//----------------------------------- Function definitions ----------------------------------//
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the deepest available domain component of a path
|
||||||
|
*
|
||||||
|
* @since 3.0.0
|
||||||
|
*
|
||||||
|
* @param string path Path to parse
|
||||||
|
* @return string|null Extracted domain
|
||||||
|
*/
|
||||||
|
function pathToDomain(path) {
|
||||||
|
var parts = path.split(/\//).reverse();
|
||||||
|
for (var key in parts) {
|
||||||
|
if (parts[key].indexOf("@") >= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var t = TldJS.parse(parts[key]);
|
||||||
|
if (t.isValid && t.domain !== null) {
|
||||||
|
return t.hostname;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set badge text with the number of matching password entries
|
||||||
|
*
|
||||||
|
* @since 3.0.0
|
||||||
|
*
|
||||||
|
* @param int tabId Tab id
|
||||||
|
*/
|
||||||
|
async function updateMatchingPasswordsCount(tabId) {
|
||||||
|
try {
|
||||||
|
const settings = await getFullSettings();
|
||||||
|
var response = await hostAction(settings, "list");
|
||||||
|
if (response.status != "ok") {
|
||||||
|
throw new Error(JSON.stringify(response));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get tab info
|
||||||
|
let currentDomain = undefined;
|
||||||
|
try {
|
||||||
|
const tab = await chrome.tabs.get(tabId);
|
||||||
|
currentDomain = new URL(tab.url).hostname;
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`Unable to determine domain of the tab with id ${tabId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
let matchedPasswordsCount = 0;
|
||||||
|
for (var storeId in response.data.files) {
|
||||||
|
for (var key in response.data.files[storeId]) {
|
||||||
|
const login = response.data.files[storeId][key].replace(/\.gpg$/i, "");
|
||||||
|
const domain = pathToDomain(storeId + "/" + login);
|
||||||
|
const inCurrentDomain =
|
||||||
|
currentDomain === domain || currentDomain.endsWith("." + domain);
|
||||||
|
const recent = settings.recent[sha1(currentDomain + sha1(storeId + sha1(login)))];
|
||||||
|
if (recent || inCurrentDomain) {
|
||||||
|
matchedPasswordsCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchedPasswordsCount) {
|
||||||
|
// Set badge for the current tab
|
||||||
|
chrome.browserAction.setBadgeText({
|
||||||
|
text: "" + matchedPasswordsCount,
|
||||||
|
tabId: tabId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy text to clipboard
|
* Copy text to clipboard
|
||||||
*
|
*
|
||||||
@@ -89,6 +170,11 @@ function saveRecent(settings, login, remove = false) {
|
|||||||
|
|
||||||
// save to local storage
|
// save to local storage
|
||||||
localStorage.setItem("recent", JSON.stringify(settings.recent));
|
localStorage.setItem("recent", JSON.stringify(settings.recent));
|
||||||
|
|
||||||
|
// a new entry was added to the popup matching list, need to refresh the count
|
||||||
|
if (!login.inCurrentDomain && login.recent.count === 1) {
|
||||||
|
updateMatchingPasswordsCount(settings.tab.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -226,6 +312,83 @@ function getLocalSettings() {
|
|||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get full settings from the extension and host application
|
||||||
|
*
|
||||||
|
* @since 3.0.0
|
||||||
|
*
|
||||||
|
* @return object Full settings object
|
||||||
|
*/
|
||||||
|
async function getFullSettings() {
|
||||||
|
var settings = getLocalSettings();
|
||||||
|
var configureSettings = Object.assign(deepCopy(settings), {
|
||||||
|
defaultStore: {}
|
||||||
|
});
|
||||||
|
var response = await hostAction(configureSettings, "configure");
|
||||||
|
if (response.status != "ok") {
|
||||||
|
throw new Error(JSON.stringify(response)); // TODO handle host error
|
||||||
|
}
|
||||||
|
settings.version = response.version;
|
||||||
|
if (Object.keys(settings.stores).length > 0) {
|
||||||
|
// there are user-configured stores present
|
||||||
|
for (var storeId in settings.stores) {
|
||||||
|
if (response.data.storeSettings.hasOwnProperty(storeId)) {
|
||||||
|
var fileSettings = JSON.parse(response.data.storeSettings[storeId]);
|
||||||
|
if (typeof settings.stores[storeId].settings !== "object") {
|
||||||
|
settings.stores[storeId].settings = {};
|
||||||
|
}
|
||||||
|
var storeSettings = settings.stores[storeId].settings;
|
||||||
|
for (var settingKey in fileSettings) {
|
||||||
|
if (!storeSettings.hasOwnProperty(settingKey)) {
|
||||||
|
storeSettings[settingKey] = fileSettings[settingKey];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// no user-configured stores, so use the default store
|
||||||
|
settings.stores.default = {
|
||||||
|
id: "default",
|
||||||
|
name: "pass",
|
||||||
|
path: response.data.defaultStore.path
|
||||||
|
};
|
||||||
|
var fileSettings = JSON.parse(response.data.defaultStore.settings);
|
||||||
|
if (typeof settings.stores.default.settings !== "object") {
|
||||||
|
settings.stores.default.settings = {};
|
||||||
|
}
|
||||||
|
var storeSettings = settings.stores.default.settings;
|
||||||
|
for (var settingKey in fileSettings) {
|
||||||
|
if (!storeSettings.hasOwnProperty(settingKey)) {
|
||||||
|
storeSettings[settingKey] = fileSettings[settingKey];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill recent data
|
||||||
|
for (var storeId in settings.stores) {
|
||||||
|
var when = localStorage.getItem("recent:" + storeId);
|
||||||
|
if (when) {
|
||||||
|
settings.stores[storeId].when = JSON.parse(when);
|
||||||
|
} else {
|
||||||
|
settings.stores[storeId].when = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
settings.recent = localStorage.getItem("recent");
|
||||||
|
if (settings.recent) {
|
||||||
|
settings.recent = JSON.parse(settings.recent);
|
||||||
|
} else {
|
||||||
|
settings.recent = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill current tab info
|
||||||
|
try {
|
||||||
|
settings.tab = (await chrome.tabs.query({ active: true, currentWindow: true }))[0];
|
||||||
|
settings.host = new URL(settings.tab.url).hostname;
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get most relevant setting value
|
* Get most relevant setting value
|
||||||
*
|
*
|
||||||
@@ -602,73 +765,8 @@ async function receiveMessage(message, sender, sendResponse) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var settings = getLocalSettings();
|
|
||||||
try {
|
try {
|
||||||
var configureSettings = Object.assign(deepCopy(settings), {
|
const settings = await getFullSettings();
|
||||||
defaultStore: {}
|
|
||||||
});
|
|
||||||
var response = await hostAction(configureSettings, "configure");
|
|
||||||
if (response.status != "ok") {
|
|
||||||
throw new Error(JSON.stringify(response)); // TODO handle host error
|
|
||||||
}
|
|
||||||
settings.version = response.version;
|
|
||||||
if (Object.keys(settings.stores).length > 0) {
|
|
||||||
// there are user-configured stores present
|
|
||||||
for (var storeId in settings.stores) {
|
|
||||||
if (response.data.storeSettings.hasOwnProperty(storeId)) {
|
|
||||||
var fileSettings = JSON.parse(response.data.storeSettings[storeId]);
|
|
||||||
if (typeof settings.stores[storeId].settings !== "object") {
|
|
||||||
settings.stores[storeId].settings = {};
|
|
||||||
}
|
|
||||||
var storeSettings = settings.stores[storeId].settings;
|
|
||||||
for (var settingKey in fileSettings) {
|
|
||||||
if (!storeSettings.hasOwnProperty(settingKey)) {
|
|
||||||
storeSettings[settingKey] = fileSettings[settingKey];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// no user-configured stores, so use the default store
|
|
||||||
settings.stores.default = {
|
|
||||||
id: "default",
|
|
||||||
name: "pass",
|
|
||||||
path: response.data.defaultStore.path
|
|
||||||
};
|
|
||||||
var fileSettings = JSON.parse(response.data.defaultStore.settings);
|
|
||||||
if (typeof settings.stores.default.settings !== "object") {
|
|
||||||
settings.stores.default.settings = {};
|
|
||||||
}
|
|
||||||
var storeSettings = settings.stores.default.settings;
|
|
||||||
for (var settingKey in fileSettings) {
|
|
||||||
if (!storeSettings.hasOwnProperty(settingKey)) {
|
|
||||||
storeSettings[settingKey] = fileSettings[settingKey];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill recent data
|
|
||||||
for (var storeId in settings.stores) {
|
|
||||||
var when = localStorage.getItem("recent:" + storeId);
|
|
||||||
if (when) {
|
|
||||||
settings.stores[storeId].when = JSON.parse(when);
|
|
||||||
} else {
|
|
||||||
settings.stores[storeId].when = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
settings.recent = localStorage.getItem("recent");
|
|
||||||
if (settings.recent) {
|
|
||||||
settings.recent = JSON.parse(settings.recent);
|
|
||||||
} else {
|
|
||||||
settings.recent = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill current tab info
|
|
||||||
try {
|
|
||||||
settings.tab = (await chrome.tabs.query({ active: true, currentWindow: true }))[0];
|
|
||||||
settings.host = new URL(settings.tab.url).hostname;
|
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
handleMessage(settings, message, sendResponse);
|
handleMessage(settings, message, sendResponse);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// handle error
|
// handle error
|
||||||
|
@@ -23,8 +23,9 @@
|
|||||||
"open_in_tab": false
|
"open_in_tab": false
|
||||||
},
|
},
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"clipboardWrite",
|
|
||||||
"activeTab",
|
"activeTab",
|
||||||
|
"tabs",
|
||||||
|
"clipboardWrite",
|
||||||
"nativeMessaging",
|
"nativeMessaging",
|
||||||
"notifications",
|
"notifications",
|
||||||
"webRequest",
|
"webRequest",
|
||||||
|
@@ -21,8 +21,9 @@
|
|||||||
"open_in_tab": false
|
"open_in_tab": false
|
||||||
},
|
},
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"clipboardWrite",
|
|
||||||
"activeTab",
|
"activeTab",
|
||||||
|
"tabs",
|
||||||
|
"clipboardWrite",
|
||||||
"nativeMessaging",
|
"nativeMessaging",
|
||||||
"notifications",
|
"notifications",
|
||||||
"webRequest",
|
"webRequest",
|
||||||
|
Reference in New Issue
Block a user