Skip to content

Commit 11bc84d

Browse files
committed
load favicons from browser (chrome-based browsers) or DuckDuckGo API (Firefox)
1 parent 184c948 commit 11bc84d

4 files changed

Lines changed: 37 additions & 21 deletions

File tree

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,15 @@ Manually:
5454
- there are NO settings, deal with it
5555
- just displays your bookmarks in multiple columns for quick access from the new tab page
5656
- organize your bookmarks in folders under Bookmarks Bar to display in columns (or don't)
57-
- displays favicons for your bookmarks (via Google API)
57+
- displays favicons for your bookmarks
5858
- displays small "show" / "hide" toggle in bottom right corner to hide all your bookmarks (for when you are sharing your screen or someone is looking over your shoulder)
5959
- when you hide bookmarks [a random photo](https://unsplash.com/documentation#get-a-random-photo) from [wallpaper topic on Unsplash](https://unsplash.com/t/wallpapers?utm_source=chrome-new-tab-page-bookmarks&utm_medium=referral) is displayed once every 15 minutes
6060

6161
## Privacy and permission justification
6262

63-
Extension requires [permissions to access your bookmarks](https://developer.chrome.com/docs/extensions/reference/bookmarks/) to display them on the new tab page. Your bookmarks do not leave your device. It loads favicons via Google API ([using t2.gstatic.com/faviconV2 like this](https://t2.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=http://github.com&size=16)) and might disclose domains & subdomains (not full URLs) from your bookmarks to Google.
63+
Extension requires [permissions to access your bookmarks](https://developer.chrome.com/docs/extensions/reference/bookmarks/) to display them on the new tab page. Your bookmarks do not leave your device.
64+
65+
In Chrome-based browsers it retrieves [favicons](https://developer.chrome.com/docs/extensions/how-to/ui/favicons) directly from browser. In Firefox it loads favicons via DuckDuckGO API ([they don’t track you, ever](https://duckduckgo.com/duckduckgo-help-pages/privacy/favicons)).
6466

6567
Wallpaper images from Unsplash API are loaded via proxy. All requests are anonymous and rate-limited via IP address hash. Image is loaded directly from Unsplash CDN and is [tracked by Unsplash](https://unsplash.com/documentation#hotlinking).
6668

chrome/src/manifest.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "New Tab Page Bookmarks",
33
"description": "Displays your bookmarks in multiple columns for quick access from the new tab page",
4-
"version": "1.9",
4+
"version": "1.10",
55
"manifest_version": 3,
66
"author": "https://github.com/mathio",
77
"chrome_url_overrides": {
@@ -13,5 +13,5 @@
1313
"48": "icon48.png",
1414
"128": "icon128.png"
1515
},
16-
"permissions": ["bookmarks"]
16+
"permissions": ["bookmarks", "favicon"]
1717
}

firefox/src/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "New Tab Page Bookmarks",
33
"description": "Displays your bookmarks in multiple columns for quick access from the new tab page",
4-
"version": "1.9",
4+
"version": "1.10",
55
"manifest_version": 2,
66
"author": "https://github.com/mathio",
77
"chrome_url_overrides": {

src/index.js

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,20 @@
11
import { decodeBlurHash } from './fast-blurhash.js'
22

3-
const iconUrl = (href) => {
4-
const { origin } = new URL(href)
5-
return `https://t2.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=${origin}&size=32`
3+
const isFirefox = typeof browser !== 'undefined'
4+
5+
export const DEFAULT_ICON_SVG = `data:image/svg+xml,${encodeURIComponent(
6+
'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="gray" viewBox="0 0 16 16"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8m7.5-6.923c-.67.204-1.335.82-1.887 1.855A8 8 0 0 0 5.145 4H7.5zM4.09 4a9.3 9.3 0 0 1 .64-1.539 7 7 0 0 1 .597-.933A7.03 7.03 0 0 0 2.255 4zm-.582 3.5c.03-.877.138-1.718.312-2.5H1.674a7 7 0 0 0-.656 2.5zM4.847 5a12.5 12.5 0 0 0-.338 2.5H7.5V5zM8.5 5v2.5h2.99a12.5 12.5 0 0 0-.337-2.5zM4.51 8.5a12.5 12.5 0 0 0 .337 2.5H7.5V8.5zm3.99 0V11h2.653c.187-.765.306-1.608.338-2.5zM5.145 12q.208.58.468 1.068c.552 1.035 1.218 1.65 1.887 1.855V12zm.182 2.472a7 7 0 0 1-.597-.933A9.3 9.3 0 0 1 4.09 12H2.255a7 7 0 0 0 3.072 2.472M3.82 11a13.7 13.7 0 0 1-.312-2.5h-2.49c.062.89.291 1.733.656 2.5zm6.853 3.472A7 7 0 0 0 13.745 12H11.91a9.3 9.3 0 0 1-.64 1.539 7 7 0 0 1-.597.933M8.5 12v2.923c.67-.204 1.335-.82 1.887-1.855q.26-.487.468-1.068zm3.68-1h2.146c.365-.767.594-1.61.656-2.5h-2.49a13.7 13.7 0 0 1-.312 2.5m2.802-3.5a7 7 0 0 0-.656-2.5H12.18c.174.782.282 1.623.312 2.5zM11.27 2.461c.247.464.462.98.64 1.539h1.835a7 7 0 0 0-3.072-2.472c.218.284.418.598.597.933M10.855 4a8 8 0 0 0-.468-1.068C9.835 1.897 9.17 1.282 8.5 1.077V4z"/></svg>'
7+
)}`
8+
9+
const getFavicon = (pageUrl) => {
10+
if (isFirefox) {
11+
const { hostname } = new URL(pageUrl)
12+
return `https://icons.duckduckgo.com/ip3/${hostname}.ico`
13+
}
14+
const url = new URL(chrome.runtime.getURL('/_favicon/'))
15+
url.searchParams.set('pageUrl', pageUrl)
16+
url.searchParams.set('size', '32')
17+
return url.toString()
618
}
719

820
const addElm = (parent, tag, text, attributes) => {
@@ -21,13 +33,13 @@ const addElm = (parent, tag, text, attributes) => {
2133

2234
const renderNode =
2335
(root) =>
24-
({ title, children, url: href }) => {
36+
async ({ title, children, url: href }) => {
2537
const li = addElm(root, 'li')
2638

2739
if (children) {
2840
addElm(li, 'h3', title)
2941
const ul = addElm(li, 'ul')
30-
children.forEach(renderNode(ul))
42+
await Promise.all(children.map(renderNode(ul)))
3143
} else if (href) {
3244
const parentUl = li.closest('ul')
3345
if (parentUl?.id === 'root') {
@@ -38,14 +50,18 @@ const renderNode =
3850
link.href = href
3951

4052
const icon = document.createElement('img')
41-
icon.src = iconUrl(href)
4253
icon.alt = title.substring(0, 1)
4354
link.append(icon)
4455

4556
const text = document.createTextNode(` ${title}`)
4657
link.append(text)
4758

4859
li.append(link)
60+
61+
icon.src = getFavicon(href)
62+
icon.onerror = () => {
63+
icon.src = DEFAULT_ICON_SVG
64+
}
4965
}
5066
}
5167

@@ -60,17 +76,15 @@ const renderBookmarksBar = async () => {
6076
}
6177

6278
//
63-
rootNode.children
64-
.filter(
65-
(item) =>
66-
item.folderType === 'bookmarks-bar' || // chrome: find using folderType, there can be more than one (https://developer.chrome.com/blog/bookmarks-sync-changes)
67-
item.id === 'toolbar_____' // firefox: find using ID
68-
)
69-
.forEach((bar) => {
70-
bar.children.forEach(renderNode(domRoot))
71-
})
79+
const bars = rootNode.children.filter(
80+
(item) =>
81+
item.folderType === 'bookmarks-bar' || // chrome: find using folderType, there can be more than one (https://developer.chrome.com/blog/bookmarks-sync-changes)
82+
item.id === 'toolbar_____' // firefox: find using ID
83+
)
7284

73-
console.log(rootNode)
85+
for (const bar of bars) {
86+
await Promise.all(bar.children.map(renderNode(domRoot)))
87+
}
7488
}
7589

7690
const TOGGLE_KEY_NAME = 'hideBookmarksBar'

0 commit comments

Comments
 (0)