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

Bugfix FXIOS-10996, FXIOS-11158 Fix app crashes in HistoryPanel due to duplicate Site identifiers #24116

Merged

Conversation

ih-codes
Copy link
Collaborator

@ih-codes ih-codes commented Jan 13, 2025

📜 Tickets

Jira ticket
Github issue

Also fixes FXIOS-11158

💡 Description

This PR refactors the Site class and its inheriting classes to instead be a Site struct which synthesizes its Equatable and Hashable methods.

The purpose of this change is to clean up some old code, and ultimately ensure all Sites are unique and strictly Identifiable for diffing (as required on iOS 18 with the History Panel diffable data source).

This work should hopefully resolve crashes we are seeing on Sentry due to "duplicate" Sites, which is a regression on a past fix in this PR: #23494 (possibly caused by a backport related to the xcode 16 upgrade).

Main Changes:

  • The Site object was built as a class, with several types inheriting from it and adding additional functionality (suggested sites, sponsored tiles, pinned sites…). This felt a bit non-swifty. I tried making Site a protocol at first, but ran into issues with making other types that had any Site Equatable, as a concrete type was needed. So I opted to make an enum to identify each SiteType.
  • Because Site is inside the Storage target, it was necessary for me to move the associated types for suggested, sponsored, and pinned sites to Storage as well (as Storage cannot access Client).
  • Updated code in Client, Storage, and ClientTests for the refactor.
  • Refactored the Site implementation to use Codable instead of custom encode/decode methods (used for the WidgetExtension)
  • Refactored Site implementation to automatically synthesize Hashable and Equatable conformance vs. explicit implementations, which has caused bugs in the past due to the implementations not being symmetrical
  • Cleaned up some naming for clarity (e.g. "sponsored tiles" -> "sponsored sites").

Testing

Test cases are listed in the attached JIRA ticket. 🙏

📝 Checklist

You have to check all boxes before merging

  • Filled in the above information (tickets numbers and description of your work)
  • Updated the PR name to follow our PR naming guidelines
  • Wrote unit tests and/or ensured the tests suite is passing
  • When working on UI, I checked and implemented accessibility (minimum Dynamic Text and VoiceOver)
  • If needed, I updated documentation / comments for complex code and public methods
  • If needed, added a backport comment (example @Mergifyio backport release/v120)

…wift Int ID's larger than Int32 will crash.
… Improved existing tests and refactored force unwraps. Fixed misleading assert messages.
… protocol. This allows for automatic conformance to Equatable and Hashable, which should reduce programmer error and crashes related to diffable data sources.
…er, TabManagerMiddleware, ContextMenuHelper, SponsoredTileTelemetry, UnifiedAdsCallbackTelemetry, JumpBackInViewModel, HistoryPanelViewModel, and the WidgetExtension to use the new Site type.
…tension factory helper for creating a sponsored site using a Contile in the Client target,
…10996-refactor-history-panel-hashable-conformance

# Conflicts:
#	firefox-ios/Storage/SQL/SQLiteHistoryFactories.swift
…ration from previous versions). Add documentation.
…tor-history-panel-hashable-conformance

# Conflicts:
#	firefox-ios/Client/Frontend/Home/Homepage Rebuild/TopSites/TopSitesManager.swift
#	firefox-ios/Client/Frontend/Home/Homepage Rebuild/TopSites/TopSitesMiddleware.swift
#	firefox-ios/Client/Frontend/Library/Bookmarks/BookmarksViewController.swift
#	firefox-ios/Storage/SQL/SQLiteHistoryFactories.swift
#	firefox-ios/firefox-ios-tests/Tests/StorageTests/TestSQLitePinnedSites.swift
@ih-codes ih-codes requested a review from Cramsden January 13, 2025 21:58
@ih-codes ih-codes requested a review from a team as a code owner January 13, 2025 21:58
@mobiletest-ci-bot
Copy link

mobiletest-ci-bot commented Jan 13, 2025

Warnings
⚠️ Pull Request size seems relatively large. If this Pull Request contains multiple changes, please split each into separate PR will helps faster, easier review. Consider using epic branches for work that would affect main.
Messages
📖 Project coverage: 34.22%
📖 Edited 66 files
📖 Created 6 files

Client.app: Coverage: 32.32

File Coverage
BrowserViewController.swift 3.52% ⚠️
ContextMenuConfiguration.swift 90.0%
TabManagerMiddleware.swift 7.12% ⚠️
Profile.swift 22.21% ⚠️
SearchLoader.swift 0.0% ⚠️
BackForwardListViewController.swift 12.79% ⚠️
SearchHighlightItem.swift 0.0% ⚠️
HomepageContextMenuHelper.swift 4.02% ⚠️
PhotonActionSheetViewModel.swift 15.56% ⚠️
MainMenuActionHelper.swift 0.0% ⚠️
TopSitesProvider.swift 98.03%
LegacyBookmarksPanel.swift 39.64% ⚠️
BookmarksViewController.swift 8.9% ⚠️
ContextMenuState.swift 2.82% ⚠️
TopSitesViewModel.swift 33.48% ⚠️
SearchViewController.swift 23.44% ⚠️
HistoryPanelViewModel.swift 84.46%
HomepageContextMenuProtocol.swift 0.0% ⚠️
UnifiedAdsCallbackTelemetry.swift 80.52%
TopSitesManager.swift 100.0%
SearchTelemetry.swift 5.34% ⚠️
LegacyHomepageViewController.swift 36.42% ⚠️
TopSite.swift 46.94% ⚠️
BookmarksViewModel.swift 27.73% ⚠️
ReaderPanel.swift 29.13% ⚠️
TopSitesMiddleware.swift 100.0%
SearchViewModel.swift 50.0% ⚠️
TopSitesWidgetManager.swift 0.0% ⚠️
TopSiteState.swift 30.36% ⚠️
SponsoredTileTelemetry.swift 89.83%
JumpBackInViewModel.swift 38.36% ⚠️
RecentlyClosedTabsPanel.swift 7.32% ⚠️
GoogleTopSiteManager.swift 80.0%
Site+createSponsoredSite.swift 100.0%
TopSiteItemCell.swift 0.0% ⚠️
TopSiteCell.swift 0.0% ⚠️
PocketViewModel.swift 72.95%
TopSitesDataAdaptor.swift 96.51%

CredentialProvider.appex: Coverage: 21.41

File Coverage
Profile.swift 22.21% ⚠️

NotificationService.appex: Coverage: 25.99

File Coverage
Profile.swift 22.21% ⚠️

ShareTo.appex: Coverage: 31.42

File Coverage
Profile.swift 22.21% ⚠️

WidgetKitExtension.appex: Coverage: 7.05

File Coverage
TopSitesProvider.swift 0.0% ⚠️

libStorage.a: Coverage: 56.34

File Coverage
RustPlaces.swift 73.23%
SiteType.swift 66.67%
SponsoredSiteInfo.swift 100.0%
SQLiteHistoryFactories.swift 85.71%
Site.swift 60.61%
PageMetadata.swift 33.33% ⚠️
DefaultSuggestedSites.swift 90.91%

Generated by 🚫 Danger Swift against 8bc6779

Copy link
Contributor

mergify bot commented Jan 15, 2025

This pull request has conflicts when rebasing. Could you fix it @ih-codes? 🙏

Copy link
Collaborator Author

@ih-codes ih-codes left a comment

Choose a reason for hiding this comment

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

Ok, made the requested changes, ready for another look! cc @Cramsden

// Return our bundled G icon for all of the Google Suite.
// Parse example: "https://drive.google.com/drive/home" > "drive.google.com" > "google"
imageResource = GoogleTopSiteManager.Constants.faviconResource
switch topSite.site.type {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good idea, will add that!

self.viewModel.profile.places.deleteBookmarksWithURL(url: site.url) >>== {
site.setBookmarked(false)
}
self.viewModel.profile.places.deleteBookmarksWithURL(url: site.url) >>== {}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

😅 Looks like these are the options:
_ = self.viewModel.profile.places.deleteBookmarksWithURL(url: site.url)
and
self.viewModel.profile.places.deleteBookmarksWithURL(url: site.url) >>== {}

I'll go with the first one since it's slightly shorter / more obvious... and going to add a comment too.

Comment on lines 138 to 140
if let domainMap = DefaultSuggestedSites.urlMap[site.url],
let localizedURL = domainMap[locale.identifier] {
return Site(copiedFromSite: site, withLocalizedURLString: localizedURL)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good call out, since this looks wrong at a glance. But I think we're ok here. This copies the current Site (so .suggestedSite remains the same) and just updates the (nonmutable) url property on Site... a little hokey but I really didn't want url to be a var on the struct.

Comment on lines +27 to +29
return URL(string: url, invalidCharacters: false) ?? URL(string: "about:blank")!
default:
return URL(string: url, invalidCharacters: false)?.domainURL ?? URL(string: "about:blank")!
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I honestly have no idea. Everyone wants to remove the force unwraps but I just copied what was already in the app and moved it here... 😬 I'm open to suggestions. 😆

Comment on lines +15 to +37
public var isPinnedSite: Bool {
switch self {
case .pinnedSite:
return true
default:
return false
}
}

public var isSponsoredSite: Bool {
switch self {
case .sponsoredSite:
return true
default:
return false
}
}

public var isSuggestedSite: Bool {
switch self {
case .suggestedSite:
return true
default:
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good idea, I'll add that! 👍

@@ -722,7 +722,7 @@ extension RustPlaces {
}
// Note: FXIOS-10740 Necessary to have unique Site ID iOS 18 HistoryPanel crash with diffable data sources
let hashValue = "\(info.url)_\(info.timestamp)".hashValue
let site = Site(id: hashValue, url: info.url, title: title)
var site = Site(id: hashValue, url: info.url, title: title, type: .basic)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Hmmm yes I see what you mean...

This is one of the rare places we explicitly want to define the ID rather than auto generate one, which is why it's like this...

I'm going to make the default init private, and we can add an optional first ID param to the createBasicSite factory method.

case .pinnedSite, .suggestedSite:
imageResource = topSite.site.faviconResource
default:
if let siteURL = URL(string: siteURLString),
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This was to override ALL google favicons with our embedded google image (since google has notoriously low quality favicons for some reason)... The pinned google tile should have a faviconResource already attached. (Actually, funny story, it didn't, but thanks to this comment I double checked and fixed it at the call site in GoogleTopSiteManager.swift 😂 )

@ih-codes ih-codes requested a review from Cramsden January 17, 2025 17:21
@ih-codes
Copy link
Collaborator Author

@Cramsden Argh, looks like some more merge conflicts on this branch -- will fix tomorrow morning. Just FYI if you re-review tonight.

Copy link
Contributor

mergify bot commented Jan 22, 2025

This pull request has conflicts when rebasing. Could you fix it @ih-codes? 🙏

Copy link
Contributor

@Cramsden Cramsden left a comment

Choose a reason for hiding this comment

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

I think there are some merge conflicts that need to be resolved but (lol just saw your above comment!) the additional changes look good to me!

@ih-codes ih-codes requested a review from a team as a code owner January 22, 2025 20:34
@ih-codes ih-codes requested a review from clarmso January 22, 2025 20:34
@ih-codes ih-codes added the Do Not Merge ⛔️ This issue is a work in progress and is not ready to land label Jan 22, 2025
@ih-codes ih-codes force-pushed the ih/FXIOS-10996-refactor-history-panel-hashable-conformance branch from 8e88fd8 to 3f75507 Compare January 22, 2025 22:07
Copy link
Contributor

mergify bot commented Jan 22, 2025

This pull request has conflicts when rebasing. Could you fix it @ih-codes? 🙏

…red tiles / unified search telemetry.

Merge remote-tracking branch 'mozilla/main' into ih/FXIOS-10996-refactor-history-panel-hashable-conformance

# Conflicts:
#	firefox-ios/Client/Frontend/Home/Homepage Rebuild/TopSites/TopSiteState.swift
#	firefox-ios/Client/Frontend/Home/TopSites/DataManagement/UnifiedAds/UnifiedAdsCallbackTelemetry.swift
#	firefox-ios/Client/Frontend/Home/TopSites/TopSite.swift
#	firefox-ios/Client/Frontend/Home/TopSites/TopSitesViewModel.swift
#	firefox-ios/Client/Telemetry/SponsoredTileTelemetry.swift
#	firefox-ios/firefox-ios-tests/Tests/ClientTests/Helpers/RatingPromptManagerTests.swift
#	firefox-ios/firefox-ios-tests/Tests/ClientTests/SponsoredTileTelemetryTests.swift
…tor-history-panel-hashable-conformance

# Conflicts:
#	firefox-ios/Client.xcodeproj/project.pbxproj
@ih-codes ih-codes removed request for a team and clarmso January 23, 2025 15:05
@ih-codes ih-codes removed the Do Not Merge ⛔️ This issue is a work in progress and is not ready to land label Jan 23, 2025
@ih-codes ih-codes merged commit 9ef8272 into main Jan 23, 2025
12 of 13 checks passed
@ih-codes ih-codes deleted the ih/FXIOS-10996-refactor-history-panel-hashable-conformance branch January 23, 2025 17:11
@ih-codes ih-codes changed the title Bugfix FXIOS-10996 Fix app crashes in HistoryPanel due to duplicate Site identifiers Bugfix FXIOS-10996, FXIOS-11158 Fix app crashes in HistoryPanel due to duplicate Site identifiers Jan 23, 2025
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.

3 participants