Skip to content

Commit

Permalink
fix support of Chrome Dev/Beta/Canary builds
Browse files Browse the repository at this point in the history
  • Loading branch information
mallexxx committed Dec 20, 2023
1 parent d5125d1 commit 8b1a452
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 27 deletions.
9 changes: 8 additions & 1 deletion DuckDuckGo/DataImport/DataImport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,14 @@ enum DataImport {
}

static func < (lhs: DataImport.BrowserProfile, rhs: DataImport.BrowserProfile) -> Bool {
return lhs.profileName.localizedCompare(rhs.profileName) == .orderedAscending
// first sort by profiles folder name if multiple profiles folders are present (Chrome, Chrome Canary…)
let profilesDirName1 = lhs.profileURL.deletingLastPathComponent().lastPathComponent
let profilesDirName2 = rhs.profileURL.deletingLastPathComponent().lastPathComponent
if profilesDirName1 == profilesDirName2 {
return lhs.profileName.localizedCompare(rhs.profileName) == .orderedAscending
} else {
return profilesDirName1.localizedCompare(profilesDirName2) == .orderedAscending
}
}

static func == (lhs: DataImport.BrowserProfile, rhs: DataImport.BrowserProfile) -> Bool {
Expand Down
52 changes: 30 additions & 22 deletions DuckDuckGo/DataImport/ThirdPartyBrowser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -179,19 +179,22 @@ enum ThirdPartyBrowser: CaseIterable {

func browserProfiles(applicationSupportURL: URL? = nil) -> DataImport.BrowserProfileList {
var potentialProfileURLs: [URL] {
guard let profilePath = profilesDirectory(applicationSupportURL: applicationSupportURL) else { return [] }
let potentialProfileURLs = (try? FileManager.default.contentsOfDirectory(at: profilePath,
includingPropertiesForKeys: nil,
options: [.skipsHiddenFiles]).filter(\.hasDirectoryPath)) ?? []
return potentialProfileURLs + [profilePath]
let fm = FileManager()
let profilesDirectories = self.profilesDirectories(applicationSupportURL: applicationSupportURL)
return profilesDirectories.reduce(into: []) { result, profilesDir in
result.append(contentsOf: (try? fm.contentsOfDirectory(at: profilesDir,
includingPropertiesForKeys: nil,
options: [.skipsHiddenFiles])
.filter(\.hasDirectoryPath)) ?? [])
} + profilesDirectories
}

let profiles: [DataImport.BrowserProfile]
switch self {
case .safari, .safariTechnologyPreview:
// Safari is an exception, as it may need permissions granted before being able to read the contents of the profile path. To be safe,
// return the profile anyway and check the file system permissions when preparing to import.
guard let profileURL = profilesDirectory(applicationSupportURL: applicationSupportURL) else {
guard let profileURL = profilesDirectories(applicationSupportURL: applicationSupportURL).first else {
assertionFailure("Unexpected nil profileURL for Safari")
profiles = []
break
Expand Down Expand Up @@ -239,23 +242,28 @@ enum ThirdPartyBrowser: CaseIterable {

// Returns the URL to the profiles for a given browser. This directory will contain a list of directories, each representing a profile.
// swiftlint:disable:next cyclomatic_complexity
func profilesDirectory(applicationSupportURL: URL? = nil) -> URL? {
func profilesDirectories(applicationSupportURL: URL? = nil) -> [URL] {
let applicationSupportURL = applicationSupportURL ?? URL.nonSandboxApplicationSupportDirectoryURL
switch self {
case .brave: return applicationSupportURL.appendingPathComponent("BraveSoftware/Brave-Browser/")
case .chrome: return applicationSupportURL.appendingPathComponent("Google/Chrome/")
case .chromium: return applicationSupportURL.appendingPathComponent("Chromium/")
case .coccoc: return applicationSupportURL.appendingPathComponent("Coccoc/")
case .edge: return applicationSupportURL.appendingPathComponent("Microsoft Edge/")
case .firefox: return applicationSupportURL.appendingPathComponent("Firefox/Profiles/")
case .opera: return applicationSupportURL.appendingPathComponent("com.operasoftware.Opera/")
case .operaGX: return applicationSupportURL.appendingPathComponent("com.operasoftware.OperaGX/")
case .safari: return URL.nonSandboxLibraryDirectoryURL.appendingPathComponent("Safari/")
case .safariTechnologyPreview: return URL.nonSandboxLibraryDirectoryURL.appendingPathComponent("SafariTechnologyPreview/")
case .tor: return applicationSupportURL.appendingPathComponent("TorBrowser-Data/Browser/")
case .vivaldi: return applicationSupportURL.appendingPathComponent("Vivaldi/")
case .yandex: return applicationSupportURL.appendingPathComponent("Yandex/YandexBrowser/")
case .bitwarden, .lastPass, .onePassword7, .onePassword8: return nil
return switch self {
case .brave: [applicationSupportURL.appendingPathComponent("BraveSoftware/Brave-Browser/")]
case .chrome: [
applicationSupportURL.appendingPathComponent("Google/Chrome/"),
applicationSupportURL.appendingPathComponent("Google/Chrome Beta/"),
applicationSupportURL.appendingPathComponent("Google/Chrome Dev/"),
applicationSupportURL.appendingPathComponent("Google/Chrome Canary/"),
]
case .chromium: [applicationSupportURL.appendingPathComponent("Chromium/")]
case .coccoc: [applicationSupportURL.appendingPathComponent("Coccoc/")]
case .edge: [applicationSupportURL.appendingPathComponent("Microsoft Edge/")]
case .firefox: [applicationSupportURL.appendingPathComponent("Firefox/Profiles/")]
case .opera: [applicationSupportURL.appendingPathComponent("com.operasoftware.Opera/")]
case .operaGX: [applicationSupportURL.appendingPathComponent("com.operasoftware.OperaGX/")]
case .safari: [URL.nonSandboxLibraryDirectoryURL.appendingPathComponent("Safari/")]
case .safariTechnologyPreview: [URL.nonSandboxLibraryDirectoryURL.appendingPathComponent("SafariTechnologyPreview/")]
case .tor: [applicationSupportURL.appendingPathComponent("TorBrowser-Data/Browser/")]
case .vivaldi: [applicationSupportURL.appendingPathComponent("Vivaldi/")]
case .yandex: [applicationSupportURL.appendingPathComponent("Yandex/YandexBrowser/")]
case .bitwarden, .lastPass, .onePassword7, .onePassword8: []
}
}

Expand Down
21 changes: 17 additions & 4 deletions DuckDuckGo/DataImport/View/DataImportProfilePicker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,14 @@ struct DataImportProfilePicker: View {

private let profiles: [DataImport.BrowserProfile]
@Binding private var selectedProfile: DataImport.BrowserProfile?
private let shouldDisplayFolderName: Bool

init(profileList: DataImport.BrowserProfileList?, selectedProfile: Binding<DataImport.BrowserProfile?>) {
self.profiles = profileList?.validImportableProfiles ?? []
self._selectedProfile = selectedProfile
shouldDisplayFolderName = Set(self.profiles.map {
$0.profileURL.deletingLastPathComponent()
}).count > 1
}

var body: some View {
Expand All @@ -39,7 +43,16 @@ struct DataImportProfilePicker: View {
selectedProfile = profiles[safe: $0]
}) {
ForEach(profiles.indices, id: \.self) { idx in
Text(profiles[idx].profileName)
// display profiles folder name if multiple profiles folders are present (Chrome, Chrome Canary…)
if shouldDisplayFolderName {
Text(profiles[idx].profileName + " ")
+ Text(profiles[idx].profileURL
.deletingLastPathComponent().lastPathComponent)
.font(.system(size: 10))
.fontWeight(.light)
} else {
Text(profiles[idx].profileName)
}
}
} label: {}
.pickerStyle(.menu)
Expand All @@ -52,11 +65,11 @@ struct DataImportProfilePicker: View {
#Preview {
DataImportProfilePicker(profileList: .init(browser: .chrome, profiles: [
.init(browser: .chrome,
profileURL: URL(fileURLWithPath: "/test/Default Profile")),
profileURL: URL(fileURLWithPath: "/Chrome/Default Profile")),
.init(browser: .chrome,
profileURL: URL(fileURLWithPath: "/test/Profile 1")),
profileURL: URL(fileURLWithPath: "/Chrome Dev/Profile 1")),
.init(browser: .chrome,
profileURL: URL(fileURLWithPath: "/test/Profile 2")),
profileURL: URL(fileURLWithPath: "/Chrome Canary/Profile 2")),
], validateProfileData: { _ in { .init(logins: .available, bookmarks: .available) } }), selectedProfile: Binding {
.init(browser: .chrome,
profileURL: URL(fileURLWithPath: "/test/Profile 1"))
Expand Down

0 comments on commit 8b1a452

Please sign in to comment.