forked from AgregoreWeb/extension-agregore-history
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathbackground.js
More file actions
151 lines (125 loc) · 3.7 KB
/
background.js
File metadata and controls
151 lines (125 loc) · 3.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/* global chrome, IDBKeyRange, AbortController */
// Import the idb library
importScripts('idb.min.js');
if (!globalThis.browser) globalThis.browser = chrome;
const TAB_CHECK_DELAY = 100;
const HISTORY_DB = 'history';
const HISTORY_VERSION = 1;
const HISTORY_STORE = 'navigated';
const MAX_RESULTS = 8;
const FORBIDDEN_PROTOCOLS = [
'peersky:',
'browser:',
'chrome-extension:',
'chrome:',
'devtools:',
'file:',
'about:',
'view-source:',
'data:',
'blob:'
];
main();
async function main() {
const db = await idb.openDB(HISTORY_DB, HISTORY_VERSION, {
upgrade
});
let aborter = null;
chrome.webNavigation.onCompleted.addListener(onCompleted);
// Open view.html in a new tab when extension icon is clicked
chrome.action.onClicked.addListener(() => {
chrome.tabs.create({ url: chrome.runtime.getURL('view.html') });
});
globalThis.db = db;
globalThis.search = search;
async function* search(query = '', maxResults = MAX_RESULTS, _signal) {
let signal = _signal;
if (!signal) {
if (aborter) aborter.abort();
aborter = new AbortController();
signal = aborter.signal;
}
let sent = 0;
const seen = new Set();
const regexText = query.split(' ').reduce((result, letter) => `${result}.*${letter}`, '');
const filter = new RegExp(regexText, 'iu');
let start = Date.now();
const range = IDBKeyRange.upperBound(start);
let cursor = await db
.transaction(HISTORY_STORE, 'readonly')
.store.index('timestamp')
.openCursor(range, 'prev');
while (cursor) {
const {key, value} = cursor;
start = key;
const { search: searchString, url } = value;
if (searchString.match(filter) && !seen.has(url)) {
seen.add(url);
yield value;
sent++;
if (sent >= maxResults) break;
const range = IDBKeyRange.upperBound(start);
cursor = await db
.transaction(HISTORY_STORE, 'readonly')
.store.index('timestamp')
.openCursor(range, 'prev');
}
if (signal && signal.aborted) {
break;
}
cursor = await cursor.continue();
}
}
async function onCompleted ({ timeStamp, tabId }) {
await delay(TAB_CHECK_DELAY);
const tab = await getTab(tabId);
const { url, title } = tab;
const parsedUrl = parseUrlSafe(url);
if (shouldSkipUrl(parsedUrl)) return;
const { host, protocol, pathname } = parsedUrl;
const historyItem = {
host,
protocol,
pathname,
url,
title,
timestamp: timeStamp,
search: `${url} ${title}`
};
// console.log('Navigation event', historyItem)
await db.add(HISTORY_STORE, historyItem);
}
}
async function upgrade (db) {
const store = db.createObjectStore(HISTORY_STORE, {
// The 'id' property of the object will be the key.
keyPath: 'id',
// If it isn't explicitly set, create a value by auto incrementing.
autoIncrement: true
});
store.createIndex('search', 'search', { unique: false });
store.createIndex('timestamp', 'timestamp', { unique: false });
store.createIndex('url', 'url', { unique: false });
store.createIndex('title', 'title', { unique: false });
store.createIndex('host', 'host', { unique: false });
store.createIndex('protocol', 'protocol', { unique: false });
}
async function getTab (id) {
return new Promise((resolve) => {
chrome.tabs.get(id, resolve);
});
}
async function delay (ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
function parseUrlSafe (rawUrl) {
try {
return new URL(rawUrl);
} catch (error) {
return null;
}
}
function shouldSkipUrl (parsedUrl) {
if (!parsedUrl) return true;
return FORBIDDEN_PROTOCOLS.includes(parsedUrl.protocol);
}