Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Favicons #388

Merged
merged 46 commits into from
Jan 16, 2022
Merged

Favicons #388

merged 46 commits into from
Jan 16, 2022

Conversation

tomasstrba
Copy link
Contributor

@tomasstrba tomasstrba commented Jan 3, 2022

Task/Issue URL: https://app.asana.com/0/72649045549333/1200560847598933/f
Tech Design URL: https://app.asana.com/0/481882893211075/1201323850042734/f
CC:

Description:
This project aim to solve following problems with favicons:

  • wrong and inconsistent favicons (related to URL paths),
  • no permanent cache,
  • downsampling problems related to holding only the biggest favicon in cache,
  • the need to clean the favicon cache when switching dark/aqua mode
  • privacy issues related to burning (keeping unnecessary favicons of burning domains in cache)

Steps to test this PR:
Test the overall experience

  1. Visit various websites and look at favicons
  2. There shouldn't be visual artifacts from downsampling large favicons (as we currently have in the released build(

Test small favicons for the tab bar

  1. Visit https://www.nintendo.com/switch/
  2. Make sure there is Super Mario favicon

Test medium favicons on the homepage

  1. Add https://www.nintendo.com/switch/ to favorites
  2. Make sure there is red "Nintendo" favicon

Test 16x16 favicon

  1. Visit https://www.ebay.com/
  2. Make sure website has a favicon in the tab bar

Test different favicon for subdomains

  1. Visit https://sme.sk and https://sport.sme.sk
  2. Make sure sme.sk has a different favicon than sport.sme.sk

Test permanent store of favicons

  1. Add a website to favorites while visiting it
  2. Make sure it has a favicon displayed on the homepage
  3. Quit browser
  4. Open browser again and make sure favicon is displayed on the homepage
  5. When visiting this website, it's favicon should appear in the tab bar immediately

Test dark/aqua favicon alternatives

  1. Visit https://googlechromelabs.github.io/dark-mode-toggle/demo/index.html
  2. Switch macOS visual mode
  3. Refresh the website and make sure favicon changed

Test burning of favicons
Burning should remove favicon from cache and permanent cache if the appropriate host isn't in bookmarks or fireproofed domains.
This can be tested well with slow internet connection. Website has no favicon in cache if there is an empty space for few seconds in the tab tar

Note: Unit tests in progress


Internal references:

Software Engineering Expectations
Technical Design Template
When ready for review, remember to post the PR in MM

@bstandaert-ddg
Copy link

Hi Tom, a couple issues I've noticed so far:

  1. On the last step of your testing procedure, https://googlechromelabs.github.io/dark-mode-toggle/demo/index.html doesn't seem to be working as expected. If I switch the system theme from light to dark and reload, I get the dark icon as expected. However, if I switch back to the light theme and reload again, I still see the dark icon.

I'm also wondering about the need to reload here - in Firefox, the favicon changes immediately. Some websites make use of real-time favicon updates - for example, Github shows the build status in the favicon, so that you can leave a tab in the background and see when the build finishes, and I remember seeing other websites use the favicon to show unread notifications. Was this discussed somewhere already?

  1. The favicon cache doesn't handle very large favicons well. To test, open these links each in their own tab:

https://favicon-test-61298f.netlify.app/favicon-test.html?icon=duck-10000px.png%3Fq%3D1
https://favicon-test-61298f.netlify.app/favicon-test.html?icon=duck-10000px.png%3Fq%3D2
https://favicon-test-61298f.netlify.app/favicon-test.html?icon=duck-10000px.png%3Fq%3D3
https://favicon-test-61298f.netlify.app/favicon-test.html?icon=duck-10000px.png%3Fq%3D4

  • The app hangs for 5s+ after each page load, and hangs again when opening new tabs.
  • The app starts using 10gb+ of memory (which makes my computer unresponsive).
  • In the container directory, Database.sqlite expands in size to 2gb.
  • If I close those tabs and then restart the browser, it hangs and uses 4gb+ of memory to open the database. The only way to make the browser run normally again is to manually delete Database.sqlite or use the fire button (but if the browser is frozen, using the fire button won't be possible).

No normal website should run into this (the favicon in the demo site is 10000x10000px), but it seems like it could be misused pretty easily.

  1. I don't think it's exactly in the scope of this PR, but if the icon is small or non-square, Firefox and Chrome both resize it to fit a square. DDG doesn't:

Screen Shot 2022-01-04 at 12 24 54 AM

Examples:
https://favicon-test-61298f.netlify.app/favicon-test.html?icon=duck-2px.png
https://favicon-test-61298f.netlify.app/favicon-test.html?icon=duck-narrow.png
https://favicon-test-61298f.netlify.app/favicon-test.html?icon=duck-wide.png

The other steps on your testing list all looked good. I'll do some testing with popular websites tomorrow and make sure there aren't any issues with specific sites.

@tomasstrba tomasstrba self-assigned this Jan 4, 2022
@bstandaert-ddg
Copy link

I ran through a list of the top 50 domains to check the icons with this PR. The tab icons look great! I didn't see any issues there. The homepage icons have a few issues compared to Safari - it seems that in some cases, Safari is able to find a high-resolution icon, while DDG is using a low-resolution one. Full results: https://docs.google.com/spreadsheets/d/10ps0nxrIg_-AuOshFrFQ5sSXXYucKNM3L_ndTiOOMjk/edit

@tomasstrba
Copy link
Contributor Author

Awesome Ben! Thanks a lot, this is super helpful. I'm diving into it.

@Bunn
Copy link
Collaborator

Bunn commented Jan 10, 2022

@tomasstrba LGTM! Just one thing that stands out to me, when saving https://www.ebay.com/ the favorite icon looks really tiny, but when I checked on Safari it wasn't even there, FF does displays it, but not the favicon, so maybe it's not a real issue?

ebay

@tomasstrba
Copy link
Contributor Author

@Bunn, yeah you are right. That also the reason why the release of this feature was paused. I am holding the mid-mortem during the O-J today to find out appropriate next steps.

@tomasstrba tomasstrba assigned tomasstrba and unassigned Bunn Jan 11, 2022
@tomasstrba
Copy link
Contributor Author

@Bunn, we agreed to keep the homepage experience same as it is in the released app. I have already pushed the commit.

There are also few remaining things to resolve related to the #380. From now on, I don't want to disturb you with this PR so next time this is ready for a review I will assign to Alex. Thanks a lot for your time and help here! 🙏 🙏 🙏

@Bunn
Copy link
Collaborator

Bunn commented Jan 11, 2022

For sure! Feel free to ping me if you need help :)

URLSession.default.dataTask(with: faviconUrl) { data, _, error in
guard let data = data, error == nil else {
assertionFailure("Fetching failed")
return
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should group.leave here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You won't believe but I woke up in the middle of the night and realized this

Comment on lines 206 to 236
let group = DispatchGroup()
var favicons = [Favicon]()

faviconLinks.forEach { faviconLink in
guard let faviconUrl = URL(string: faviconLink.href) else {
return
}

group.enter()
URLSession.default.dataTask(with: faviconUrl) { data, _, error in
guard let data = data, error == nil else {
assertionFailure("Fetching failed")
return
}

let favicon = Favicon(identifier: UUID(),
url: faviconUrl,
image: NSImage(data: data),
relationString: faviconLink.rel,
documentUrl: documentUrl,
dateCreated: Date())
DispatchQueue.main.async {
favicons.append(favicon)
group.leave()
}
mainQueueCompletion(favicons)
}.resume()
}

group.notify(queue: .main) {
completion(favicons)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see no particular reasoning in synchronising fetched results using the Main Queue, although it shouldn't be a big deal speedwise, but still..
It used to be possible to asynchronously access array elements directly which creates no data race (favicons[idx] = favicon) because each element is pre-allocated, but now Thread Sanitizer gives a warning about favicons access data race. I've played with it a bit and found out that using withUnsafeMutablePointer(to: &favicons[idx]) solves the issue - this creates no data races and requires no extra threads synchronisation, see the suggested approach:

Suggested change
let group = DispatchGroup()
var favicons = [Favicon]()
faviconLinks.forEach { faviconLink in
guard let faviconUrl = URL(string: faviconLink.href) else {
return
}
group.enter()
URLSession.default.dataTask(with: faviconUrl) { data, _, error in
guard let data = data, error == nil else {
assertionFailure("Fetching failed")
return
}
let favicon = Favicon(identifier: UUID(),
url: faviconUrl,
image: NSImage(data: data),
relationString: faviconLink.rel,
documentUrl: documentUrl,
dateCreated: Date())
DispatchQueue.main.async {
favicons.append(favicon)
group.leave()
}
mainQueueCompletion(favicons)
}.resume()
}
group.notify(queue: .main) {
completion(favicons)
}
var favicons = [Favicon?].init(repeating: nil, count: faviconLinks.count)
let group = DispatchGroup()
faviconLinks.enumerated().forEach { idx, faviconLink in
guard let faviconUrl = URL(string: faviconLink.href) else {
return
}
group.enter()
withUnsafeMutablePointer(to: &favicons[idx]) { favicon in
URLSession.default.dataTask(with: faviconUrl) { data, _, error in
defer {
group.leave()
}
guard let data = data, error == nil else {
assertionFailure("Fetching failed")
return
}
favicon.pointee = Favicon(identifier: UUID(),
url: faviconUrl,
image: NSImage(data: data),
relationString: faviconLink.rel,
documentUrl: documentUrl,
dateCreated: Date())
}.resume()
}
}
group.notify(queue: .main) {
completion(favicons.compactMap { $0 })
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this tip and also for finished suggestion. I have mixed feelings about this change because:

  • The reason I implemented this safe is to eliminate any possibilities of data races we previosly had with favicons
  • The suggestion speeds up adding to the array, unfortunately new array needs to be created on the completion (Hard to tell if we even speed things up overall)
  • Users won't see any difference
  • The readability of the suggestion is worse then the original code.

It was very interesting to see that there is a possibility to do it but I would stick with the original solution. What do you think?

@mallexxx
Copy link
Collaborator

I've checked the latest URLSession commit, Looks good to me overall, just see my comments above

group.enter()
URLSession.default.dataTask(with: faviconUrl) { data, _, error in
guard let data = data, error == nil else {
assertionFailure("Fetching failed")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also I would expect it to be quite ok for a fetch request to fail, assertion here seems unneded

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, true

@tomasstrba
Copy link
Contributor Author

Note: I rolled back SecureVault exceptions since there was no reasonable way to do it without a major architecture change. It will be resolved as a follow-up task

@tomasstrba tomasstrba merged commit 63a3d35 into develop Jan 16, 2022
@tomasstrba tomasstrba deleted the tom/favicons branch January 16, 2022 21:30
muodov added a commit that referenced this pull request Mar 17, 2024
Task/Issue URL:
https://app.asana.com/0/1206853951101015/1206853951101015
Autoconsent Release:
https://github.com/duckduckgo/autoconsent/releases/tag/v10.3.0


## Description
Updates Autoconsent to version
[v10.3.0](https://github.com/duckduckgo/autoconsent/releases/tag/v10.3.0).

### Autoconsent v10.3.0 release notes
#### 🚀 Enhancement

- DDG release automation
[#389](duckduckgo/autoconsent#389)
([@muodov](https://github.com/muodov))
- Bump the dev-dependencies group with 4 updates
[#390](duckduckgo/autoconsent#390)
([@dependabot[bot]](https://github.com/dependabot[bot]))

#### 🐛 Bug Fix

- Fix infinite reload for OneTrust sites
[#393](duckduckgo/autoconsent#393)
([@muodov](https://github.com/muodov))
- Script to crawl page text content in multiple languages.
[#386](duckduckgo/autoconsent#386)
([@sammacbeth](https://github.com/sammacbeth))
- Update Asana sync action
[#388](duckduckgo/autoconsent#388)
([@sammacbeth](https://github.com/sammacbeth))

#### Authors: 3

- [@dependabot[bot]](https://github.com/dependabot[bot])
- Maxim Tsoy ([@muodov](https://github.com/muodov))
- Sam Macbeth ([@sammacbeth](https://github.com/sammacbeth))


## Steps to test
This release has been tested during Autoconsent development. You can
check the release notes for more information.

Co-authored-by: muodov <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants