diff --git a/.githooks/pre-push b/.githooks/pre-push index 07acb8ee6c49..51024faf94cf 100755 --- a/.githooks/pre-push +++ b/.githooks/pre-push @@ -1,19 +1,45 @@ #!/bin/sh -# Get the current branch -get_current_branch() { - current_branch=$(git symbolic-ref --short HEAD) - echo "$current_branch" +# get the URL of a remote +get_remote_url() { + remote_name=$1 + git remote get-url "$remote_name" } -# Save the current branch -current_branch=$(get_current_branch) -echo "Current branch: $current_branch" +# remote name associated with the restricted URL +find_remote_name_by_url() { + restricted_url="https://github.com/mozilla-mobile/firefox-ios.git" + for remote in $(git remote); do + remote_url=$(get_remote_url "$remote") + if [ "$remote_url" = "$restricted_url" ]; then + echo "$remote" + return + fi + done + echo "" +} + +# current tracked remote and branch for the current local branch +upstream=$(git rev-parse --abbrev-ref --symbolic-full-name @{u}) +current_tracked_remote=$(echo "$upstream" | cut -d'/' -f1) +current_tracked_branch=$(echo "$upstream" | cut -d'/' -f2) + +echo "Current tracked remote: $current_tracked_remote" +echo "Current tracked branch: $current_tracked_branch" + +restricted_remote=$(find_remote_name_by_url) +echo "Restricted remote: $restricted_remote" + +# Special Case: remote being pushed to from the first argument of the pre-push hook +push_remote_my=$1 +echo "Push remote: $restricted_remote" -# Check if the current branch is the restricted branch 'main' -if [ "$current_branch" = "main" ]; then - echo "Direct pushes to the 'main' branch are not allowed." - exit 1 +if [ "$current_tracked_branch" = "main" ]; then + # if the push remote is the restricted remote + if [ "$push_remote_my" = "$restricted_remote" ]; then + echo "Direct pushes to the 'main' branch on the specified remote are not allowed." + exit 1 + fi fi exit 0 \ No newline at end of file diff --git a/.github/workflows/firefox-ios-ui-tests.yml b/.github/workflows/firefox-ios-ui-tests.yml index 3b71fc9270ca..437c06a105ac 100644 --- a/.github/workflows/firefox-ios-ui-tests.yml +++ b/.github/workflows/firefox-ios-ui-tests.yml @@ -40,7 +40,8 @@ jobs: -scheme ${{ env.xcodebuild_scheme }} \ -target ${{ env.xcodebuild_target }} \ -derivedDataPath ~/DerivedData \ - -destination 'platform=iOS Simulator,name=${{ env.ios_simulator_default }},OS=${{ env.ios_version }}' + -destination 'platform=iOS Simulator,name=${{ env.ios_simulator_default }},OS=${{ env.ios_version }}' \ + COMPILER_INDEX_STORE_ENABLE=NO CODE_SIGN_IDENTITY= CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO ARCHS="arm64" working-directory: ${{ env.browser }} - name: Compress Derived Data id: compress-dd @@ -61,7 +62,7 @@ jobs: strategy: fail-fast: false matrix: - ios_simulator: ['iPhone 15 Plus'] + ios_simulator: ['iPhone 15 Plus', 'iPad Pro 13-inch (M4)'] steps: - name: Check out source code uses: actions/checkout@v4.1.7 @@ -70,7 +71,7 @@ jobs: run: | gem install xcpretty -v 0.3.0 pip install blockkit==1.9.1 - npm install -g junit-report-merger@7.0.0 + npm install -g junit-report-merger@7.0.0 - name: Setup Xcode id: xcode run: | @@ -99,7 +100,7 @@ jobs: -destination 'platform=iOS Simulator,name=${{ matrix.ios_simulator }},OS=${{ env.ios_version }}' \ -testPlan Smoketest1 \ -resultBundlePath ${{ env.test_results_directory }}/results-smoketest1 \ - | tee xcodebuild.log | xcpretty -r junit --output ./junit-smoketest1.xml && exit ${PIPESTATUS[0]} + | tee xcodebuild-smoketest1.log | xcpretty -r junit --output ./junit-smoketest1.xml && exit ${PIPESTATUS[0]} working-directory: ${{ env.browser }} continue-on-error: true - name: Run Smoketest2 @@ -113,7 +114,7 @@ jobs: -destination 'platform=iOS Simulator,name=${{ matrix.ios_simulator }},OS=${{ env.ios_version }}' \ -testPlan Smoketest2 \ -resultBundlePath ${{ env.test_results_directory }}/results-smoketest2 \ - | tee xcodebuild.log | xcpretty -r junit --output ./junit-smoketest2.xml && exit ${PIPESTATUS[0]} + | tee xcodebuild-smoketest2.log | xcpretty -r junit --output ./junit-smoketest2.xml && exit ${PIPESTATUS[0]} working-directory: ${{ env.browser }} continue-on-error: true - name: Run Smoketest3 @@ -127,7 +128,7 @@ jobs: -destination 'platform=iOS Simulator,name=${{ matrix.ios_simulator }},OS=${{ env.ios_version }}' \ -testPlan Smoketest3 \ -resultBundlePath ${{ env.test_results_directory }}/results-smoketest3 \ - | tee xcodebuild.log | xcpretty -r junit --output ./junit-smoketest3.xml && exit ${PIPESTATUS[0]} + | tee xcodebuild-smoketest3.log | xcpretty -r junit --output ./junit-smoketest3.xml && exit ${PIPESTATUS[0]} working-directory: ${{ env.browser }} continue-on-error: true - name: Run Smoketest4 @@ -141,7 +142,7 @@ jobs: -destination 'platform=iOS Simulator,name=${{ matrix.ios_simulator }},OS=${{ env.ios_version }}' \ -testPlan Smoketest4 \ -resultBundlePath ${{ env.test_results_directory }}/results-smoketest4 \ - | tee xcodebuild.log | xcpretty -r junit --output ./junit-smoketest4.xml && exit ${PIPESTATUS[0]} + | tee xcodebuild-smoketest4.log | xcpretty -r junit --output ./junit-smoketest4.xml && exit ${PIPESTATUS[0]} working-directory: ${{ env.browser }} continue-on-error: true - name: Determine pass/fail status @@ -167,20 +168,21 @@ jobs: python ../test-fixtures/ci/convert_junit_to_markdown.py --smoke --github --${{ env.browser }} ./combined.xml ./github.md cat github.md >> $GITHUB_STEP_SUMMARY python ../test-fixtures/ci/convert_junit_to_markdown.py --smoke --slack --${{ env.browser }} ./combined.xml ./slack.json + mv ./combined.xml "junit-smoketests-${{ matrix.ios_simulator }}-`date +"%Y-%m-%d"`.xml" working-directory: ${{ env.browser }} - name: Upload junit files id: upload-junit uses: actions/upload-artifact@v4.3.3 with: name: ${{ env.browser }}-smoketests-${{ matrix.ios_simulator }}-junit-${{ github.run_number }} - path: ${{ env.browser }}/junit-*.xml + path: ${{ env.browser }}/junit-smoketests-*.xml retention-days: 90 - name: Upload log file id: upload-log uses: actions/upload-artifact@v4.3.3 with: name: ${{ env.browser }}-smoketests-${{ matrix.ios_simulator }}-xcodebuildlog-${{ github.run_number }} - path: ${{ env.browser }}/xcodebuild.log + path: ${{ env.browser }}/xcodebuild-smoketest*.log retention-days: 90 - name: Report to Slack id: slack @@ -203,81 +205,83 @@ jobs: exit ${{ steps.passfail.outcome == 'success' && '0' || '1' }} run-fullfunctionaltests: - name: Run full functional tests - runs-on: macos-14 - needs: compile - strategy: - fail-fast: false - matrix: - ios_simulator: ['iPhone 15 Plus'] - steps: - - name: Check out source code - uses: actions/checkout@v4.1.7 - - name: Install packages - id: packages - run: | - gem install xcpretty -v 0.3.0 - pip install blockkit==1.9.1 - - name: Get derived data - id: download-derived-data - uses: actions/download-artifact@v4 - with: - name: xcode-cache-deriveddata-${{ github.workflow }}-${{ github.sha }} - - name: Decompress derived data - id: decompress-dd - run: | - tar xzf derived-data.tar.gz -C / - - name: Run tests - id: run-tests - run: | - xcodebuild \ - test-without-building \ - -scheme ${{ env.xcodebuild_scheme }} \ - -target ${{ env.xcodebuild_target }} \ - -derivedDataPath ~/DerivedData \ - -destination 'platform=iOS Simulator,name=${{ matrix.ios_simulator }},OS=${{ env.ios_version }}' \ - -testPlan FullFunctionalTestPlan \ - -resultBundlePath ${{ env.test_results_directory }}/results \ - | tee xcodebuild.log | xcpretty -r junit && exit ${PIPESTATUS[0]} - working-directory: ${{ env.browser }} - continue-on-error: true - - name: Print test report - id: test-report - run: | - python ../test-fixtures/ci/convert_junit_to_markdown.py --github --${{ env.browser }} ./build/reports/junit.xml ./github.md - cat github.md >> $GITHUB_STEP_SUMMARY - python ../test-fixtures/ci/convert_junit_to_markdown.py --slack --${{ env.browser }} ./build/reports/junit.xml ./slack.json - working-directory: ${{ env.browser }} - - name: Upload junit files - id: upload-junit - uses: actions/upload-artifact@v4.3.3 - with: - name: ${{ env.browser }}-fullfunctional-${{ matrix.ios_simulator }}-junit-${{ github.run_number }} - path: ${{ env.browser }}/junit-*.xml - retention-days: 90 - - name: Upload log file - id: upload-log - uses: actions/upload-artifact@v4.3.3 - with: - name: ${{ env.browser }}-fullfunctional-${{ matrix.ios_simulator }}-xcodebuildlog-${{ github.run_number }} - path: ${{ env.browser }}/xcodebuild.log - retention-days: 90 - - name: Report to Slack - id: slack - uses: slackapi/slack-github-action@v1.26.0 - with: - payload-file-path: ${{ env.browser }}/slack.json - env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK - ios_simulator: ${{ matrix.ios_simulator }} - pass_fail: ${{ steps.run-tests.outcome == 'success' && ':white_check_mark:' || ':x:' }} - xcodebuild_test_plan: FullFunctionalTestPlan - ref_name: ${{ github.ref_name }} - repository: ${{ github.repository }} - run_id: ${{ github.run_id }} - server_url: ${{ github.server_url }} - sha: ${{ github.sha }} - - name: Return fail status if a test fails - run: | - exit ${{ steps.run-tests.outcome == 'success' && '0' || '1' }} \ No newline at end of file + name: Run full functional tests + runs-on: macos-14 + needs: compile + strategy: + fail-fast: false + matrix: + ios_simulator: ['iPhone 15 Pro Max'] + steps: + - name: Check out source code + uses: actions/checkout@v4.1.7 + - name: Install packages + id: packages + run: | + gem install xcpretty -v 0.3.0 + pip install blockkit==1.9.1 + - name: Get derived data + id: download-derived-data + uses: actions/download-artifact@v4 + with: + name: xcode-cache-deriveddata-${{ github.workflow }}-${{ github.sha }} + - name: Decompress derived data + id: decompress-dd + run: | + tar xzf derived-data.tar.gz -C / + - name: Run tests + id: run-tests + run: | + xcrun simctl boot '${{ matrix.ios_simulator }}' + xcodebuild \ + test-without-building \ + -scheme ${{ env.xcodebuild_scheme }} \ + -target ${{ env.xcodebuild_target }} \ + -derivedDataPath ~/DerivedData \ + -destination 'platform=iOS Simulator,name=${{ matrix.ios_simulator }},OS=${{ env.ios_version }}' \ + -testPlan FullFunctionalTestPlan \ + -resultBundlePath ${{ env.test_results_directory }}/results \ + | tee xcodebuild.log | xcpretty -r junit && exit ${PIPESTATUS[0]} + working-directory: ${{ env.browser }} + continue-on-error: true + - name: Print test report + id: test-report + run: | + python ../test-fixtures/ci/convert_junit_to_markdown.py --github --${{ env.browser }} ./build/reports/junit.xml ./github.md + cat github.md >> $GITHUB_STEP_SUMMARY + python ../test-fixtures/ci/convert_junit_to_markdown.py --slack --${{ env.browser }} ./build/reports/junit.xml ./slack.json + mv ./build/reports/junit.xml "junit-fullfunctional-${{ matrix.ios_simulator }}-`date +"%Y-%m-%d"`.xml" + working-directory: ${{ env.browser }} + - name: Upload junit files + id: upload-junit + uses: actions/upload-artifact@v4.3.3 + with: + name: ${{ env.browser }}-fullfunctional-${{ matrix.ios_simulator }}-junit-${{ github.run_number }} + path: ${{ env.browser }}/junit-*.xml + retention-days: 90 + - name: Upload log file + id: upload-log + uses: actions/upload-artifact@v4.3.3 + with: + name: ${{ env.browser }}-fullfunctional-${{ matrix.ios_simulator }}-xcodebuildlog-${{ github.run_number }} + path: ${{ env.browser }}/xcodebuild.log + retention-days: 90 + - name: Report to Slack + id: slack + uses: slackapi/slack-github-action@v1.26.0 + with: + payload-file-path: ${{ env.browser }}/slack.json + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK + ios_simulator: ${{ matrix.ios_simulator }} + pass_fail: ${{ steps.run-tests.outcome == 'success' && ':white_check_mark:' || ':x:' }} + xcodebuild_test_plan: FullFunctionalTestPlan + ref_name: ${{ github.ref_name }} + repository: ${{ github.repository }} + run_id: ${{ github.run_id }} + server_url: ${{ github.server_url }} + sha: ${{ github.sha }} + - name: Return fail status if a test fails + run: | + exit ${{ steps.run-tests.outcome == 'success' && '0' || '1' }} \ No newline at end of file diff --git a/.github/workflows/focus-ios-ui-tests.yml b/.github/workflows/focus-ios-ui-tests.yml index bb0d41763c1c..8fa5d935d7ce 100644 --- a/.github/workflows/focus-ios-ui-tests.yml +++ b/.github/workflows/focus-ios-ui-tests.yml @@ -40,7 +40,8 @@ jobs: -scheme ${{ env.xcodebuild_scheme }} \ -target ${{ env.xcodebuild_target }} \ -derivedDataPath ~/DerivedData \ - -destination 'platform=iOS Simulator,name=${{ env.ios_simulator_default }},OS=${{ env.ios_version }}' + -destination 'platform=iOS Simulator,name=${{ env.ios_simulator_default }},OS=${{ env.ios_version }}' \ + COMPILER_INDEX_STORE_ENABLE=NO CODE_SIGN_IDENTITY= CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO ARCH="arm64" working-directory: ${{ env.browser }} - name: Save Derived Data id: upload-derived-data @@ -108,6 +109,13 @@ jobs: name: ${{ env.browser }}-${{ matrix.xcodebuild_test_plan }}-${{ matrix.ios_simulator }}-xcodebuildlog-${{ github.run_number }} path: ${{ env.browser }}/xcodebuild.log retention-days: 90 + - name: Upload junit files + id: upload-junit + uses: actions/upload-artifact@v4.3.3 + with: + name: ${{ env.browser }}-${{ matrix.xcodebuild_test_plan }}-${{ matrix.ios_simulator }}-junit-${{ github.run_number }} + path: ${{ env.browser }}/build/reports/junit.xml + retention-days: 90 - name: Report to Slack id: slack uses: slackapi/slack-github-action@v1.26.0 diff --git a/.swiftlint.yml b/.swiftlint.yml index 760c7222b5a7..f24c1f9155fa 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -82,6 +82,8 @@ only_rules: # Only enforce these rules, ignore all others - vertical_whitespace_closing_braces - vertical_whitespace_opening_braces - yoda_condition + - accessibility_label_for_image + - accessibility_trait_for_button # These rules were originally opted into. Disabling for now to get # Swiftlint up and running. diff --git a/.taskcluster.yml b/.taskcluster.yml index d053e622797a..d6aa1d32a9ac 100644 --- a/.taskcluster.yml +++ b/.taskcluster.yml @@ -108,7 +108,7 @@ tasks: $if: > tasks_for in ["action", "cron"] || (isPullRequest && pullRequestAction in ["opened", "reopened", "synchronize"]) - || (tasks_for == "github-push" && (short_head_branch == "main" || short_head_branch[:8] == "releases")) + || (tasks_for == "github-push" && short_head_branch == "main") || (tasks_for == "github-release" && releaseAction == "published") then: taskId: diff --git a/BrowserKit/Package.resolved b/BrowserKit/Package.resolved index 0d8d7a515d83..c97fbc9996a9 100644 --- a/BrowserKit/Package.resolved +++ b/BrowserKit/Package.resolved @@ -41,8 +41,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/getsentry/sentry-cocoa.git", "state" : { - "revision" : "38f4f70d07117b9f958a76b1bff278c2f29ffe0e", - "version" : "8.21.0" + "revision" : "d2ced2d961b34573ebd2ea0567a2f1408e90f0ae", + "version" : "8.34.0" } }, { diff --git a/BrowserKit/Package.swift b/BrowserKit/Package.swift index 107755d44bd9..717844cf1b8a 100644 --- a/BrowserKit/Package.swift +++ b/BrowserKit/Package.swift @@ -30,6 +30,9 @@ let package = Package( .library( name: "ToolbarKit", targets: ["ToolbarKit"]), + .library( + name: "MenuKit", + targets: ["MenuKit"]), .library( name: "ContentBlockingGenerator", targets: ["ContentBlockingGenerator"]), @@ -52,7 +55,7 @@ let package = Package( exact: "2.0.0"), .package( url: "https://github.com/getsentry/sentry-cocoa.git", - exact: "8.21.0"), + exact: "8.34.0"), .package(url: "https://github.com/nbhasin2/GCDWebServer.git", branch: "master") ], @@ -109,6 +112,13 @@ let package = Package( .testTarget( name: "ToolbarKitTests", dependencies: ["ToolbarKit"]), + .target( + name: "MenuKit", + dependencies: ["Common"], + swiftSettings: [.unsafeFlags(["-enable-testing"])]), + .testTarget( + name: "MenuKitTests", + dependencies: ["MenuKit"]), .target( name: "ContentBlockingGenerator", swiftSettings: [.unsafeFlags(["-enable-testing"])]), diff --git a/BrowserKit/Sources/Common/Constants/StandardImageIdentifiers.swift b/BrowserKit/Sources/Common/Constants/StandardImageIdentifiers.swift index 558e24127856..a94b3e8106a8 100644 --- a/BrowserKit/Sources/Common/Constants/StandardImageIdentifiers.swift +++ b/BrowserKit/Sources/Common/Constants/StandardImageIdentifiers.swift @@ -10,6 +10,7 @@ import Foundation public struct StandardImageIdentifiers { // Icon size 16x16 public struct Small { + public static let externalLink = "externalLinkSmall" public static let notificationDot = "notificationDotSmall" public static let notificationDotFill = "notificationDotFillSmall" public static let pinBadgeFill = "pinBadgeFillSmall" @@ -95,7 +96,7 @@ public struct StandardImageIdentifiers { public static let readingListAdd = "readingListAddLarge" public static let search = "searchLarge" public static let settings = "settingsLarge" - public static let shareApple = "shareAppleLarge" + public static let share = "shareLarge" public static let shield = "shieldLarge" public static let shieldSlash = "shieldSlashLarge" public static let shipping = "shippingLarge" diff --git a/BrowserKit/Sources/Common/Extensions/StringExtension.swift b/BrowserKit/Sources/Common/Extensions/StringExtension.swift index a2e9b0335c30..6894e206fca3 100644 --- a/BrowserKit/Sources/Common/Extensions/StringExtension.swift +++ b/BrowserKit/Sources/Common/Extensions/StringExtension.swift @@ -11,3 +11,15 @@ extension StringProtocol { return replacingOccurrences(of: target, with: "") } } + +public extension String { + /// Returns a new string made by removing the leading String characters contained + /// in a given character set. + func stringByTrimmingLeadingCharactersInSet(_ set: CharacterSet) -> String { + var trimmed = self + while trimmed.rangeOfCharacter(from: set)?.lowerBound == trimmed.startIndex { + trimmed.remove(at: trimmed.startIndex) + } + return trimmed + } +} diff --git a/BrowserKit/Sources/Common/Extensions/URLExtension.swift b/BrowserKit/Sources/Common/Extensions/URLExtension.swift index 0cbf64b04f0c..a4377b852a14 100644 --- a/BrowserKit/Sources/Common/Extensions/URLExtension.swift +++ b/BrowserKit/Sources/Common/Extensions/URLExtension.swift @@ -305,6 +305,15 @@ extension URL { let schemes = includeDataURIs ? ["http", "https", "data"] : ["http", "https"] return scheme.map { schemes.contains($0) } ?? false } + + /// Returns the standard location of the website's favicon. (This is the base directoy path with + /// favicon.ico appended). + public func faviconUrl() -> URL? { + if let host = host, let rootDirectoryURL = URL(string: (scheme ?? "https") + "://" + host) { + return rootDirectoryURL.appendingPathComponent("favicon.ico") + } + return nil + } } private struct ETLDEntry: CustomStringConvertible { diff --git a/BrowserKit/Sources/Common/Theming/DarkTheme.swift b/BrowserKit/Sources/Common/Theming/DarkTheme.swift index 94678d11f43d..1606c4972de2 100644 --- a/BrowserKit/Sources/Common/Theming/DarkTheme.swift +++ b/BrowserKit/Sources/Common/Theming/DarkTheme.swift @@ -47,6 +47,7 @@ private struct DarkColourPalette: ThemeColourPalette { var layerConfirmation: UIColor = FXColors.Green80 var layerWarning: UIColor = FXColors.Yellow70.withAlphaComponent(0.77) var layerError: UIColor = FXColors.Pink80 + var layerSelectedText: UIColor = FXColors.LightGrey05.withAlphaComponent(0.34) var layerSearch: UIColor = FXColors.DarkGrey80 var layerGradientURL = Gradient(colors: [ FXColors.DarkGrey80.withAlphaComponent(0), diff --git a/BrowserKit/Sources/Common/Theming/LightTheme.swift b/BrowserKit/Sources/Common/Theming/LightTheme.swift index 5f69658b0435..e12d4a554d78 100644 --- a/BrowserKit/Sources/Common/Theming/LightTheme.swift +++ b/BrowserKit/Sources/Common/Theming/LightTheme.swift @@ -36,6 +36,7 @@ private struct LightColourPalette: ThemeColourPalette { var layerConfirmation: UIColor = FXColors.Green20 var layerWarning: UIColor = FXColors.Yellow20 var layerError: UIColor = FXColors.Red10 + var layerSelectedText: UIColor = FXColors.DarkGrey05.withAlphaComponent(0.73) var layerSearch: UIColor = FXColors.LightGrey30 var layerGradientURL = Gradient(colors: [ FXColors.LightGrey30.withAlphaComponent(0), diff --git a/BrowserKit/Sources/Common/Theming/PrivateModeTheme.swift b/BrowserKit/Sources/Common/Theming/PrivateModeTheme.swift index fd3fa90d0e7b..38d3d1d25426 100644 --- a/BrowserKit/Sources/Common/Theming/PrivateModeTheme.swift +++ b/BrowserKit/Sources/Common/Theming/PrivateModeTheme.swift @@ -38,6 +38,7 @@ private struct PrivateModeColorPalette: ThemeColourPalette { var layerConfirmation: UIColor = FXColors.Green80 var layerWarning: UIColor = FXColors.Yellow70.withAlphaComponent(0.77) var layerError: UIColor = FXColors.Pink80 + var layerSelectedText: UIColor = FXColors.Violet60 var layerSearch: UIColor = FXColors.Ink90 var layerGradientURL = Gradient(colors: [ FXColors.Ink90.withAlphaComponent(0), diff --git a/BrowserKit/Sources/Common/Theming/ThemeColourPalette.swift b/BrowserKit/Sources/Common/Theming/ThemeColourPalette.swift index 9ecb5da8aecc..c627c495ec95 100644 --- a/BrowserKit/Sources/Common/Theming/ThemeColourPalette.swift +++ b/BrowserKit/Sources/Common/Theming/ThemeColourPalette.swift @@ -27,6 +27,7 @@ public protocol ThemeColourPalette { var layerConfirmation: UIColor { get } var layerWarning: UIColor { get } var layerError: UIColor { get } + var layerSelectedText: UIColor { get } var layerSearch: UIColor { get } var layerGradientURL: Gradient { get } diff --git a/BrowserKit/Sources/Common/WindowUUID+Extensions.swift b/BrowserKit/Sources/Common/WindowUUID+Extensions.swift index 543a8e1f694f..ebabbba5a5bf 100644 --- a/BrowserKit/Sources/Common/WindowUUID+Extensions.swift +++ b/BrowserKit/Sources/Common/WindowUUID+Extensions.swift @@ -10,6 +10,20 @@ import Foundation /// can be run side-by-side on iPad (once multi-window is enabled). [FXIOS-7349] public typealias WindowUUID = UUID +/// Describes a UUID available for use in a window on either iPhone or iPad. +public struct ReservedWindowUUID { + /// The UUID of the window. + public let uuid: WindowUUID + + /// True if the UUID is for a newly-created window (with no tabs on disk) + public let isNew: Bool + + public init(uuid: WindowUUID, isNew: Bool) { + self.uuid = uuid + self.isNew = isNew + } +} + public extension WindowUUID { /// Sentinel UUID value for use when a window is unavailable or unknown. /// diff --git a/BrowserKit/Sources/ComponentLibrary/BottomSheet/BottomSheetViewController.swift b/BrowserKit/Sources/ComponentLibrary/BottomSheet/BottomSheetViewController.swift index 85fda3bc2af9..8b361322e922 100644 --- a/BrowserKit/Sources/ComponentLibrary/BottomSheet/BottomSheetViewController.swift +++ b/BrowserKit/Sources/ComponentLibrary/BottomSheet/BottomSheetViewController.swift @@ -60,11 +60,13 @@ public class BottomSheetViewController: UIViewController, private lazy var scrollContentView: UIView = .build { _ in } private var contentViewBottomConstraint: NSLayoutConstraint! private var viewTranslation = CGPoint(x: 0, y: 0) + private let windowUUID: WindowUUID // MARK: Init public init(viewModel: BottomSheetViewModel, childViewController: BottomSheetChild, usingDimmedBackground: Bool = false, + windowUUID: WindowUUID, notificationCenter: NotificationProtocol = NotificationCenter.default, themeManager: ThemeManager = AppContainer.shared.resolve()) { self.viewModel = viewModel @@ -72,6 +74,7 @@ public class BottomSheetViewController: UIViewController, self.notificationCenter = notificationCenter self.themeManager = themeManager self.useDimmedBackground = usingDimmedBackground + self.windowUUID = windowUUID super.init(nibName: nil, bundle: nil) @@ -136,8 +139,7 @@ public class BottomSheetViewController: UIViewController, // MARK: - Theme public func applyTheme() { - guard let uuid = (self.view as? ThemeUUIDIdentifiable)?.currentWindowUUID else { return } - contentView.backgroundColor = themeManager.getCurrentTheme(for: uuid).colors.layer1 + contentView.backgroundColor = themeManager.getCurrentTheme(for: windowUUID).colors.layer1 sheetView.layer.shadowOpacity = viewModel.shadowOpacity if useDimmedBackground { @@ -147,7 +149,7 @@ public class BottomSheetViewController: UIViewController, } public var currentWindowUUID: WindowUUID? { - return (self.view as? ThemeUUIDIdentifiable)?.currentWindowUUID + return windowUUID } // MARK: - UIGestureRecognizerDelegate diff --git a/BrowserKit/Sources/ComponentLibrary/BottomSheet/BottomSheetViewModel.swift b/BrowserKit/Sources/ComponentLibrary/BottomSheet/BottomSheetViewModel.swift index bd758995d8ee..4d3a0819c823 100644 --- a/BrowserKit/Sources/ComponentLibrary/BottomSheet/BottomSheetViewModel.swift +++ b/BrowserKit/Sources/ComponentLibrary/BottomSheet/BottomSheetViewModel.swift @@ -4,13 +4,14 @@ import Foundation import UIKit +import Common /// The view model used to configure a `BottomSheetViewController` public struct BottomSheetViewModel { - private struct UX { - static let cornerRadius: CGFloat = 8 - static let animationTransitionDuration: CGFloat = 0.3 - static let shadowOpacity: Float = 0.3 + public struct UX { + public static let cornerRadius: CGFloat = 8 + public static let animationTransitionDuration: CGFloat = 0.3 + public static let shadowOpacity: Float = 0.3 } public var cornerRadius: CGFloat @@ -21,23 +22,15 @@ public struct BottomSheetViewModel { public var closeButtonA11yLabel: String public var closeButtonA11yIdentifier: String - public init(closeButtonA11yLabel: String, closeButtonA11yIdentifier: String) { - cornerRadius = BottomSheetViewModel.UX.cornerRadius - animationTransitionDuration = BottomSheetViewModel.UX.animationTransitionDuration - backgroundColor = .clear - shouldDismissForTapOutside = true - shadowOpacity = BottomSheetViewModel.UX.shadowOpacity - self.closeButtonA11yLabel = closeButtonA11yLabel - self.closeButtonA11yIdentifier = closeButtonA11yIdentifier - } - - public init(cornerRadius: CGFloat, - animationTransitionDuration: TimeInterval, - backgroundColor: UIColor, - shouldDismissForTapOutside: Bool, - shadowOpacity: Float, - closeButtonA11yLabel: String, - closeButtonA11yIdentifier: String) { + public init( + cornerRadius: CGFloat = BottomSheetViewModel.UX.cornerRadius, + animationTransitionDuration: TimeInterval = BottomSheetViewModel.UX.animationTransitionDuration, + backgroundColor: UIColor = .clear, + shouldDismissForTapOutside: Bool = true, + shadowOpacity: Float = BottomSheetViewModel.UX.shadowOpacity, + closeButtonA11yLabel: String, + closeButtonA11yIdentifier: String + ) { self.cornerRadius = cornerRadius self.animationTransitionDuration = animationTransitionDuration self.backgroundColor = backgroundColor diff --git a/BrowserKit/Sources/ComponentLibrary/Buttons/ResizableButton.swift b/BrowserKit/Sources/ComponentLibrary/Buttons/ResizableButton.swift index 62d57bd0a60f..915566c898b2 100644 --- a/BrowserKit/Sources/ComponentLibrary/Buttons/ResizableButton.swift +++ b/BrowserKit/Sources/ComponentLibrary/Buttons/ResizableButton.swift @@ -39,7 +39,7 @@ open class ResizableButton: UIButton { let heightContentInset = configuration.contentInsets.top + configuration.contentInsets.bottom var availableWidth = frame.width - widthContentInset - if let imageWidth = image(for: [])?.size.width { + if let imageWidth = image(for: [])?.size.width ?? configuration.image?.size.width { availableWidth = availableWidth - imageWidth - imagePadding } diff --git a/BrowserKit/Sources/MenuKit/MenuElement.swift b/BrowserKit/Sources/MenuKit/MenuElement.swift new file mode 100644 index 000000000000..cd0a62284134 --- /dev/null +++ b/BrowserKit/Sources/MenuKit/MenuElement.swift @@ -0,0 +1,33 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit + +public struct MenuElement: Equatable { + let iconName: String + let isEnabled: Bool + let isActive: Bool + let hasSubmenu: Bool + let a11yLabel: String + let a11yHint: String? + let a11yId: String + + // We need this init as by default the init generated by the compiler + // for the struct will be internal and can not be used outside of MenuKit + public init(iconName: String, + isEnabled: Bool, + isActive: Bool, + hasSubmenu: Bool, + a11yLabel: String, + a11yHint: String?, + a11yId: String) { + self.iconName = iconName + self.isEnabled = isEnabled + self.isActive = isActive + self.hasSubmenu = hasSubmenu + self.a11yLabel = a11yLabel + self.a11yHint = a11yHint + self.a11yId = a11yId + } +} diff --git a/BrowserKit/Sources/SiteImageView/FaviconURLProcessing/URLFetcher/FaviconURLFetcher.swift b/BrowserKit/Sources/SiteImageView/FaviconURLProcessing/URLFetcher/FaviconURLFetcher.swift index 7ecc6fb488f0..a0f5d5ea006d 100644 --- a/BrowserKit/Sources/SiteImageView/FaviconURLProcessing/URLFetcher/FaviconURLFetcher.swift +++ b/BrowserKit/Sources/SiteImageView/FaviconURLProcessing/URLFetcher/FaviconURLFetcher.swift @@ -7,7 +7,7 @@ import Fuzi /// Scrapes the HTML at a given site for images protocol FaviconURLFetcher { - /// Scraptes the HTML at the given url for a favicon image + /// Scrapes the HTML at the given url for a favicon image /// - Parameter siteURL: The web address we want to retrieve the favicon for /// - Parameter completion: Returns a result type of either a URL on success or a SiteImageError on failure func fetchFaviconURL(siteURL: URL) async throws -> URL @@ -62,8 +62,8 @@ struct DefaultFaviconURLFetcher: FaviconURLFetcher { } // Fallback to the favicon at the root of the domain - // This is a fall back because it's generally low res - if let faviconURL = URL(string: siteURL.absoluteString + "/favicon.ico", invalidCharacters: false) { + // This is a fallback because it's generally low res + if let faviconURL = siteURL.faviconUrl() { return faviconURL } diff --git a/BrowserKit/Sources/SiteImageView/ImageProcessing/HeroImageFetcher/ImageProvider.swift b/BrowserKit/Sources/SiteImageView/ImageProcessing/HeroImageFetcher/ImageProvider.swift index be856e194ffd..a22d1c67d25d 100644 --- a/BrowserKit/Sources/SiteImageView/ImageProcessing/HeroImageFetcher/ImageProvider.swift +++ b/BrowserKit/Sources/SiteImageView/ImageProcessing/HeroImageFetcher/ImageProvider.swift @@ -13,6 +13,7 @@ protocol ImageProvider { } extension NSItemProvider: ImageProvider { + @MainActor func loadObject(ofClass aClass: NSItemProviderReading.Type) async throws -> UIImage { return try await withCheckedThrowingContinuation { continuation in loadObject(ofClass: aClass) { image, error in diff --git a/BrowserKit/Sources/SiteImageView/ImageProcessing/ImageHandler.swift b/BrowserKit/Sources/SiteImageView/ImageProcessing/ImageHandler.swift index 91d1adc01f58..c198eb16c98d 100644 --- a/BrowserKit/Sources/SiteImageView/ImageProcessing/ImageHandler.swift +++ b/BrowserKit/Sources/SiteImageView/ImageProcessing/ImageHandler.swift @@ -25,7 +25,7 @@ protocol ImageHandler { /// 2. Tries to fetch from the hero image fetcher (from the web). /// 3. If all fails it throws an error /// - /// Any time the hero image is fetched, it will be cache for future usage. + /// Any time the hero image is fetched, it will be cached for future use. /// - Parameters: /// - siteURL: The site URL to fetch the hero image from /// - domain: The domain this hero image will be associated with diff --git a/BrowserKit/Sources/SiteImageView/ImageProcessing/SiteImageFetcher/SiteImageDownloader.swift b/BrowserKit/Sources/SiteImageView/ImageProcessing/SiteImageFetcher/SiteImageDownloader.swift index 874b99c1a637..6c7499713892 100644 --- a/BrowserKit/Sources/SiteImageView/ImageProcessing/SiteImageFetcher/SiteImageDownloader.swift +++ b/BrowserKit/Sources/SiteImageView/ImageProcessing/SiteImageFetcher/SiteImageDownloader.swift @@ -31,34 +31,11 @@ extension SiteImageDownloader { // Use task groups to have a timeout when downloading an image from Kingfisher // due to https://sentry.io/share/issue/951b878416374dd98eccb6fd88fd8427 group.addTask { - return try await withCheckedThrowingContinuation { continuation in - // Store a copy of the continuation to act on in the case the sleep finishes first - self.continuation = continuation - - _ = self.downloadImage(with: url) { result in - guard let continuation = self.continuation else { return } - switch result { - case .success(let imageResult): - continuation.resume(returning: imageResult) - case .failure(let error): - continuation.resume(throwing: error) - } - self.continuation = nil - } - } + return try await self.handleImageDownload(url: url) } group.addTask { - try await Task.sleep(nanoseconds: self.timeoutDelay * NSEC_PER_SEC) - try Task.checkCancellation() - let error = SiteImageError.unableToDownloadImage("Timeout reached") - self.continuation?.resume(throwing: error) - self.continuation = nil - - self.logger.log("Timeout when downloading image reached", - level: .warning, - category: .images) - throw error + try await self.handleTimeout() } // wait for the first task and cancel the other one @@ -70,6 +47,37 @@ extension SiteImageDownloader { return result } } + + private func handleImageDownload(url: URL) async throws -> any SiteImageLoadingResult { + return try await withCheckedThrowingContinuation { continuation in + // Store a copy of the continuation to act on in the case the sleep finishes first + self.continuation = continuation + + _ = self.downloadImage(with: url) { result in + guard let continuation = self.continuation else { return } + switch result { + case .success(let imageResult): + continuation.resume(returning: imageResult) + case .failure(let error): + continuation.resume(throwing: error) + } + self.continuation = nil + } + } + } + + private func handleTimeout() async throws -> any SiteImageLoadingResult { + try await Task.sleep(nanoseconds: self.timeoutDelay * NSEC_PER_SEC) + try Task.checkCancellation() + let error = SiteImageError.unableToDownloadImage("Timeout reached") + self.continuation?.resume(throwing: error) + self.continuation = nil + + self.logger.log("Timeout when downloading image reached", + level: .warning, + category: .images) + throw error + } } /// Image loading result wrapper for Kingfisher type so we have control when testing diff --git a/BrowserKit/Sources/SiteImageView/SiteImageHandler.swift b/BrowserKit/Sources/SiteImageView/SiteImageHandler.swift index b97b93ce3aa1..ece4ca173a02 100644 --- a/BrowserKit/Sources/SiteImageView/SiteImageHandler.swift +++ b/BrowserKit/Sources/SiteImageView/SiteImageHandler.swift @@ -15,6 +15,13 @@ public class DefaultSiteImageHandler: SiteImageHandler { private let urlHandler: FaviconURLHandler private let imageHandler: ImageHandler + private let serialQueue = DispatchQueue(label: "com.mozilla.DefaultSiteImageHandler") + private var _currentInFlightRequest: String? + private var currentInFlightRequest: String? { + get { return serialQueue.sync { _currentInFlightRequest } } + set { serialQueue.sync { _currentInFlightRequest = newValue } } + } + public static func factory() -> DefaultSiteImageHandler { return DefaultSiteImageHandler() } @@ -102,14 +109,25 @@ public class DefaultSiteImageHandler: SiteImageHandler { private func getFaviconImage(imageModel: SiteImageModel) async -> UIImage { do { + while let currentSiteRequest = currentInFlightRequest, + imageModel.siteURLString == currentSiteRequest { + // We are already processing a favicon request for this site + // Sleep this task until the previous request is completed + try? await Task.sleep(nanoseconds: 50_000_000) // 50ms + } + + currentInFlightRequest = imageModel.siteURLString var faviconURLImageModel = imageModel if faviconURLImageModel.faviconURL == nil { // Try to fetch the favicon URL faviconURLImageModel = try await urlHandler.getFaviconURL(site: imageModel) } - return await imageHandler.fetchFavicon(site: faviconURLImageModel) + let icon = await imageHandler.fetchFavicon(site: faviconURLImageModel) + currentInFlightRequest = nil + return icon } catch { // If no favicon URL, generate favicon without it + currentInFlightRequest = nil return await imageHandler.fetchFavicon(site: imageModel) } } diff --git a/BrowserKit/Sources/SiteImageView/SiteImageView.swift b/BrowserKit/Sources/SiteImageView/SiteImageView.swift index ac768c508345..2f365dc0f086 100644 --- a/BrowserKit/Sources/SiteImageView/SiteImageView.swift +++ b/BrowserKit/Sources/SiteImageView/SiteImageView.swift @@ -10,10 +10,11 @@ protocol SiteImageView: UIView { var uniqueID: UUID? { get set } var imageFetcher: SiteImageHandler { get set } + /// The URL string representing the currently-displayed image on the view. + /// This is `nil` if an image has been set manually. + var currentURLString: String? { get set } func updateImage(site: SiteImageModel) func setImage(imageModel: SiteImageModel) - // Avoid multiple image loading in parallel. Only start a new request if the URL string has changed - var currentURLString: String? { get set } func canMakeRequest(with siteURLString: String?) -> Bool } diff --git a/BrowserKit/Sources/TabDataStore/TabDataStore.swift b/BrowserKit/Sources/TabDataStore/TabDataStore.swift index 0b9ff6c3c93d..42ee66f72c17 100644 --- a/BrowserKit/Sources/TabDataStore/TabDataStore.swift +++ b/BrowserKit/Sources/TabDataStore/TabDataStore.swift @@ -23,7 +23,11 @@ public protocol TabDataStore { /// saved files in the directory) it is faster than fetchWindowData() and is /// preferable when only the UUIDs are needed. /// - Returns: a list of UUIDs for any saved WindowData. - func fetchWindowDataUUIDs() -> [UUID] + func fetchWindowDataUUIDs() -> [WindowUUID] + + /// Erases the on-disk data for tab windows matching the provided UUIDs. + /// - Parameter forUUIDs: the UUIDs to delete the on-disk tab files for. + func removeWindowData(forUUIDs: [WindowUUID]) async } public actor DefaultTabDataStore: TabDataStore { @@ -49,27 +53,75 @@ public actor DefaultTabDataStore: TabDataStore { // MARK: Fetching Window Data public func fetchWindowData(uuid: UUID) async -> WindowData? { - logger.log("Attempting to fetch window/tab data", level: .debug, category: .tabs) + logger.log("Attempting to fetch window data", level: .debug, category: .tabs) + + // Adding more logging for FXIOS-9517 + var shouldLogFileFailure = false // Whether pulling from the main file failed + var shouldLogBackupFailure = false // Whether pulling from the backup file failed + var fileInfoMessage = "" // Specifics of main file failure + var backupInfoMessage = "" // Specifics of backup file failure + defer { + if shouldLogFileFailure { + let errorMessage: String + errorMessage = shouldLogBackupFailure + ? "Failed to open window data (including backup data) for UUID: \(uuid)" + : "Failed to open window data (but backup recovery worked) for UUID: \(uuid)" + logger.log( + "\(errorMessage) File Info: [\(fileInfoMessage)] Backup File Info: [\(backupInfoMessage)]", + level: .fatal, + category: .tabs + ) + } + } + do { - guard let fileURL = windowURLPath(for: uuid, isBackup: false), - fileManager.fileExists(atPath: fileURL), - let windowData = parseWindowDataFile(fromURL: fileURL) else { - logger.log("Failed to open window/tab data for UUID: \(uuid)", level: .fatal, category: .tabs) + guard let fileURL = windowURLPath(for: uuid, isBackup: false) else { + fileInfoMessage += "fileURL nil" + shouldLogFileFailure = true + throw TabDataError.failedToFetchData + } + + guard fileManager.fileExists(atPath: fileURL) else { + fileInfoMessage += "file doesn't exist" + shouldLogFileFailure = true throw TabDataError.failedToFetchData } + + guard let windowData = parseWindowDataFile(fromURL: fileURL) else { + fileInfoMessage += "file parsing failed" + shouldLogFileFailure = true + throw TabDataError.failedToFetchData + } + + logger.log("Successfully fetched window data", level: .debug, category: .tabs) return windowData } catch { - logger.log("Error fetching window data: UUID = \(uuid) Error = \(error)", level: .warning, category: .tabs) - guard let backupURL = windowURLPath(for: uuid, isBackup: true), - fileManager.fileExists(atPath: backupURL), - let backupWindowData = parseWindowDataFile(fromURL: backupURL) else { + logger.log("Error fetching window data for UUID: \(uuid) Error: \(error)", level: .warning, category: .tabs) + + guard let backupURL = windowURLPath(for: uuid, isBackup: true) else { + backupInfoMessage += "backup fileURL nil" + shouldLogBackupFailure = true + return nil + } + + guard fileManager.fileExists(atPath: backupURL) else { + backupInfoMessage += "backup file doesn't exist" + shouldLogBackupFailure = true + return nil + } + + guard let backupWindowData = parseWindowDataFile(fromURL: backupURL) else { + backupInfoMessage += "backup file parsing failed" + shouldLogBackupFailure = true return nil } + + logger.log("Returned backup window data for UUID: \(uuid)", level: .debug, category: .tabs) return backupWindowData } } - nonisolated public func fetchWindowDataUUIDs() -> [UUID] { + nonisolated public func fetchWindowDataUUIDs() -> [WindowUUID] { guard let directoryURL = fileManager.windowDataDirectory(isBackup: false) else { logger.log("Could not resolve window data directory", level: .warning, category: .tabs) return [] @@ -77,12 +129,7 @@ public actor DefaultTabDataStore: TabDataStore { let fileURLs = fileManager.contentsOfDirectory(at: directoryURL) - return fileURLs.compactMap { - let file = $0.lastPathComponent - guard file.hasPrefix(filePrefix) else { return nil } - let uuidString = String(file.dropFirst(filePrefix.count)) - return UUID(uuidString: uuidString) - } + return fileURLs.compactMap { windowUUID(fromURL: $0) } } private func parseWindowDataFile(fromURL url: URL) -> WindowData? { @@ -178,6 +225,22 @@ public actor DefaultTabDataStore: TabDataStore { fileManager.removeAllFilesAt(directory: backupURL) } + public func removeWindowData(forUUIDs uuids: [WindowUUID]) async { + guard let directoryURL = fileManager.windowDataDirectory(isBackup: false) else { return } + + let fileURLs = fileManager.contentsOfDirectory(at: directoryURL) + + for url in fileURLs { + guard let uuid = windowUUID(fromURL: url) else { continue } + guard uuids.contains(where: { $0 == uuid }) else { + logger.log("Will not remove window data for UUID: \(uuid)", level: .info, category: .tabs) + continue + } + logger.log("Removing window data for UUID: \(uuid)", level: .info, category: .tabs) + fileManager.removeFileAt(path: url) + } + } + // MARK: - URL Utils private func windowURLPath(for windowID: UUID, isBackup: Bool) -> URL? { @@ -185,4 +248,15 @@ public actor DefaultTabDataStore: TabDataStore { let baseFilePath = filePrefix + windowID.uuidString return baseURL.appendingPathComponent(baseFilePath) } + + /// For a URL that points to a window tab data file, returns the associated UUID + /// for that window based on the file name. + /// - Parameter url: the URL to parse. + /// - Returns: a window UUID or nil if the URL was invalid. + private nonisolated func windowUUID(fromURL url: URL) -> WindowUUID? { + let file = url.lastPathComponent + guard file.hasPrefix(filePrefix) else { return nil } + let uuidString = String(file.dropFirst(filePrefix.count)) + return WindowUUID(uuidString: uuidString) + } } diff --git a/BrowserKit/Sources/TabDataStore/TabFileManager.swift b/BrowserKit/Sources/TabDataStore/TabFileManager.swift index d7f7ff3838f5..ffd0c16f9afa 100644 --- a/BrowserKit/Sources/TabDataStore/TabFileManager.swift +++ b/BrowserKit/Sources/TabDataStore/TabFileManager.swift @@ -81,11 +81,21 @@ public struct DefaultTabFileManager: TabFileManager { } public func windowDataDirectory(isBackup: Bool) -> URL? { - guard let containerID = BrowserKitInformation.shared.sharedContainerIdentifier else { return nil } + guard let containerID = BrowserKitInformation.shared.sharedContainerIdentifier else { + logger.log( + "Failed to get the window data container ID from BrowserKit's sharedContainerIdentifier", + level: .warning, + category: .tabs + ) + return nil + } let pathInfo = isBackup ? PathInfo.backup : PathInfo.primary - var containerURL = fileManager.containerURL(forSecurityApplicationGroupIdentifier: containerID) - containerURL = containerURL?.appendingPathComponent(PathInfo.rootDirectory) - return containerURL?.appendingPathComponent(pathInfo) + guard let containerURL = fileManager.containerURL(forSecurityApplicationGroupIdentifier: containerID) else { + logger.log("Failed to get the window data container URL", level: .warning, category: .tabs) + return nil + } + let appendedURL = containerURL.appendingPathComponent(PathInfo.rootDirectory) + return appendedURL.appendingPathComponent(pathInfo) } public func contentsOfDirectory(at path: URL) -> [URL] { @@ -144,7 +154,7 @@ public struct DefaultTabFileManager: TabFileManager { return windowData } catch { logger.log("Error decoding window data: \(error)", - level: .debug, + level: .warning, category: .tabs) throw error } diff --git a/BrowserKit/Sources/TabDataStore/TabSessionStore.swift b/BrowserKit/Sources/TabDataStore/TabSessionStore.swift index 4e15de0cd2ba..59ce84658605 100644 --- a/BrowserKit/Sources/TabDataStore/TabSessionStore.swift +++ b/BrowserKit/Sources/TabDataStore/TabSessionStore.swift @@ -10,21 +10,22 @@ public protocol TabSessionStore { /// - Parameters: /// - tabID: an ID that uniquely identifies the tab /// - sessionData: the data associated with a session, encoded as a Data object - func saveTabSession(tabID: UUID, sessionData: Data) async + func saveTabSession(tabID: UUID, sessionData: Data) /// Fetches the session data associated with a tab /// - Parameter tabID: an ID that uniquely identifies the tab /// - Returns: the data associated with a session, encoded as a Data object - func fetchTabSession(tabID: UUID) async -> Data? + func fetchTabSession(tabID: UUID) -> Data? /// Cleans up any tab session data files for tabs that are no longer open. func deleteUnusedTabSessionData(keeping: [UUID]) async } -public actor DefaultTabSessionStore: TabSessionStore { +public class DefaultTabSessionStore: TabSessionStore { let fileManager: TabFileManager let logger: Logger let filePrefix = "tab-" + private let lock = NSRecursiveLock() public init(fileManager: TabFileManager = DefaultTabFileManager(), logger: Logger = DefaultLogger.shared) { @@ -32,7 +33,7 @@ public actor DefaultTabSessionStore: TabSessionStore { self.logger = logger } - public func saveTabSession(tabID: UUID, sessionData: Data) async { + public func saveTabSession(tabID: UUID, sessionData: Data) { guard let directory = fileManager.tabSessionDataDirectory() else { return } if !fileManager.fileExists(atPath: directory) { @@ -41,6 +42,8 @@ public actor DefaultTabSessionStore: TabSessionStore { let path = directory.appendingPathComponent(filePrefix + tabID.uuidString) do { + lock.lock() + defer { lock.unlock() } try sessionData.write(to: path, options: .atomicWrite) } catch { logger.log("Failed to save session data with error: \(error.localizedDescription)", @@ -49,11 +52,13 @@ public actor DefaultTabSessionStore: TabSessionStore { } } - public func fetchTabSession(tabID: UUID) async -> Data? { + public func fetchTabSession(tabID: UUID) -> Data? { guard let path = fileManager.tabSessionDataDirectory()?.appendingPathComponent(filePrefix + tabID.uuidString) else { return nil } do { + lock.lock() + defer { lock.unlock() } return try Data(contentsOf: path) } catch { logger.log("Failed to decode session data with error: \(error.localizedDescription)", diff --git a/BrowserKit/Sources/ToolbarKit/AddressToolbar/AddressToolbar.swift b/BrowserKit/Sources/ToolbarKit/AddressToolbar/AddressToolbar.swift index 6a9ad4848c3e..96ac8d355525 100644 --- a/BrowserKit/Sources/ToolbarKit/AddressToolbar/AddressToolbar.swift +++ b/BrowserKit/Sources/ToolbarKit/AddressToolbar/AddressToolbar.swift @@ -10,4 +10,6 @@ public protocol AddressToolbar { toolbarDelegate: AddressToolbarDelegate, leadingSpace: CGFloat?, trailingSpace: CGFloat?) + + func setAutocompleteSuggestion(_ suggestion: String?) } diff --git a/BrowserKit/Sources/ToolbarKit/AddressToolbar/AddressToolbarDelegate.swift b/BrowserKit/Sources/ToolbarKit/AddressToolbar/AddressToolbarDelegate.swift index 962705a0421f..180ecc2bb4a0 100644 --- a/BrowserKit/Sources/ToolbarKit/AddressToolbar/AddressToolbarDelegate.swift +++ b/BrowserKit/Sources/ToolbarKit/AddressToolbar/AddressToolbarDelegate.swift @@ -10,4 +10,5 @@ public protocol AddressToolbarDelegate: AnyObject { func openBrowser(searchTerm: String) func openSuggestions(searchTerm: String) func addressToolbarAccessibilityActions() -> [UIAccessibilityCustomAction]? + func configureContextualHint(_ addressToolbar: BrowserAddressToolbar, for button: UIButton) } diff --git a/BrowserKit/Sources/ToolbarKit/AddressToolbar/BrowserAddressToolbar.swift b/BrowserKit/Sources/ToolbarKit/AddressToolbar/BrowserAddressToolbar.swift index ca8b9a536177..4b47d37bc953 100644 --- a/BrowserKit/Sources/ToolbarKit/AddressToolbar/BrowserAddressToolbar.swift +++ b/BrowserKit/Sources/ToolbarKit/AddressToolbar/BrowserAddressToolbar.swift @@ -64,8 +64,8 @@ public class BrowserAddressToolbar: UIView, AddressToolbar, ThemeApplicable, Loc toolbarDelegate: any AddressToolbarDelegate, leadingSpace: CGFloat?, trailingSpace: CGFloat?) { - configure(state: state) self.toolbarDelegate = toolbarDelegate + configure(state: state) updateSpacing(leading: leadingSpace, trailing: trailingSpace) } @@ -79,6 +79,10 @@ public class BrowserAddressToolbar: UIView, AddressToolbar, ThemeApplicable, Loc layoutIfNeeded() } + public func setAutocompleteSuggestion(_ suggestion: String?) { + locationView.setAutocompleteSuggestion(suggestion) + } + override public func becomeFirstResponder() -> Bool { return locationView.becomeFirstResponder() } @@ -214,6 +218,10 @@ public class BrowserAddressToolbar: UIView, AddressToolbar, ThemeApplicable, Loc // As we recreate the buttons we need to apply the theme for them to be displayed correctly button.applyTheme(theme: theme) } + + if toolbarElement.hasContextualHint == true { + toolbarDelegate?.configureContextualHint(self, for: button) + } } } @@ -247,6 +255,10 @@ public class BrowserAddressToolbar: UIView, AddressToolbar, ThemeApplicable, Loc } // MARK: - LocationViewDelegate + func locationViewDidRestoreSearchTerm(_ text: String) { + toolbarDelegate?.openSuggestions(searchTerm: text) + } + func locationViewDidEnterText(_ text: String) { toolbarDelegate?.searchSuggestions(searchTerm: text) } @@ -255,7 +267,7 @@ public class BrowserAddressToolbar: UIView, AddressToolbar, ThemeApplicable, Loc toolbarDelegate?.openSuggestions(searchTerm: text.lowercased()) } - func locationViewShouldSearchFor(_ text: String) { + func locationViewDidSubmitText(_ text: String) { guard !text.isEmpty else { return } toolbarDelegate?.openBrowser(searchTerm: text.lowercased()) @@ -265,11 +277,13 @@ public class BrowserAddressToolbar: UIView, AddressToolbar, ThemeApplicable, Loc toolbarDelegate?.addressToolbarAccessibilityActions() } + func locationViewDidCancelEditing() {} + // MARK: - ThemeApplicable public func applyTheme(theme: Theme) { backgroundColor = theme.colors.layer1 locationContainer.backgroundColor = theme.colors.layerSearch - locationDividerView.backgroundColor = theme.colors.layer2 + locationDividerView.backgroundColor = theme.colors.layer1 toolbarTopBorderView.backgroundColor = theme.colors.borderPrimary toolbarBottomBorderView.backgroundColor = theme.colors.borderPrimary locationView.applyTheme(theme: theme) diff --git a/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/Callback.swift b/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/Callback.swift new file mode 100644 index 000000000000..416c44b5ed72 --- /dev/null +++ b/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/Callback.swift @@ -0,0 +1,19 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +// Encapsulate a callback in a way that we can use it with NSTimer. +final class Callback { + private let handler: () -> Void + + init(handler: @escaping () -> Void) { + self.handler = handler + } + + @objc + func go() { + handler() + } +} diff --git a/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationTextField.swift b/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationTextField.swift index f785e9634040..2fae2d17af09 100644 --- a/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationTextField.swift +++ b/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationTextField.swift @@ -5,14 +5,68 @@ import Common import UIKit -class LocationTextField: UITextField, ThemeApplicable { +/// Delegate for the text field events. Since LocationTextField owns the UITextFieldDelegate, +/// callers must use this instead. +protocol LocationTextFieldDelegate: AnyObject { + func locationTextField(_ textField: LocationTextField, didEnterText text: String) + func locationTextFieldShouldReturn(_ textField: LocationTextField) -> Bool + func locationTextFieldShouldClear(_ textField: LocationTextField) -> Bool + func locationTextFieldDidCancel(_ textField: LocationTextField) + func locationPasteAndGo(_ textField: LocationTextField) + func locationTextFieldDidBeginEditing(_ textField: UITextField) + func locationTextFieldDidEndEditing(_ textField: UITextField) +} + +class LocationTextField: UITextField, UITextFieldDelegate, ThemeApplicable { private var tintedClearImage: UIImage? private var clearButtonTintColor: UIColor? + var autocompleteDelegate: LocationTextFieldDelegate? + + // This variable is a solution to get the right behaviour for refocusing + // the LocationTextField. The initial transition into Overlay Mode + // doesn't involve the user interacting with LocationTextField. + // Thus, we update shouldApplyCompletion in touchesBegin() to reflect whether + // the highlight is active and then the text field is updated accordingly + // in touchesEnd() (eg. applyCompletion() is called or not) + private var notifyTextChanged: (() -> Void)? + + // The last string used as a replacement in shouldChangeCharactersInRange. + private var lastReplacement: String? + private var hideCursor = false + private var isSettingMarkedText = false + + private let copyShortcutKey = "c" + // MARK: - Init override init(frame: CGRect) { super.init(frame: .zero) + super.addTarget(self, action: #selector(LocationTextField.textDidChange), for: .editingChanged) + + font = UIFont.preferredFont(forTextStyle: .body) + adjustsFontForContentSizeCategory = true clearButtonMode = .whileEditing + keyboardType = .webSearch + autocorrectionType = .no + autocapitalizationType = .none + returnKeyType = .go + tintAdjustmentMode = .normal + delegate = self + + // Disable dragging urls on iPhones because it conflicts with editing the text + if UIDevice.current.userInterfaceIdiom != .pad { + textDragInteraction?.isEnabled = false + } + + notifyTextChanged = debounce(0.1, + action: { + if self.isEditing { + self.autocompleteDelegate?.locationTextField( + self, + didEnterText: self.normalizeString(self.textWithoutSuggestion() ?? "") + ) + } + }) } required init?(coder: NSCoder) { @@ -34,25 +88,232 @@ class LocationTextField: UITextField, ThemeApplicable { override func layoutSubviews() { super.layoutSubviews() - guard let image = UIImage(named: StandardImageIdentifiers.Large.crossCircleFill) else { return } if tintedClearImage == nil { - if let clearButtonTintColor { - tintedClearImage = image.withTintColor(clearButtonTintColor) - } + tintClearButton() } + } - // Since we're unable to change the tint color of the clear image, we need to iterate through the - // subviews, find the clear button, and tint it ourselves. - // https://stackoverflow.com/questions/55046917/clear-button-on-text-field-not-accessible-with-voice-over-swift - if let clearButton = value(forKey: "_clearButton") as? UIButton { - clearButton.setImage(tintedClearImage, for: []) + override func deleteBackward() { + lastReplacement = nil + hideCursor = false + + guard markedTextRange == nil else { + // If we have an active completion, delete it without deleting any user-typed characters. + removeCompletion() + forceResetCursor() + return + } + + super.deleteBackward() + } + + override func touchesBegan(_ touches: Set, with event: UIEvent?) { + applyCompletion() + super.touchesBegan(touches, with: event) + } + + override open func caretRect(for position: UITextPosition) -> CGRect { + return hideCursor ? CGRect.zero : super.caretRect(for: position) + } + + override open func setMarkedText(_ markedText: String?, selectedRange: NSRange) { + isSettingMarkedText = true + removeCompletion() + super.setMarkedText(markedText, selectedRange: selectedRange) + isSettingMarkedText = false + } + + func setAutocompleteSuggestion(_ suggestion: String?) { + let searchText = text ?? "" + + guard let suggestion = suggestion, isEditing && markedTextRange == nil else { + hideCursor = false + return + } + + let normalized = normalizeString(searchText) + guard suggestion.hasPrefix(normalized) && normalized.count < suggestion.count else { + hideCursor = false + return + } + + let suggestionText = String(suggestion.dropFirst(normalized.count)) + setMarkedText(suggestionText, selectedRange: NSRange()) + + // Only call forceResetCursor() if `hideCursor` changes. + // Because forceResetCursor() auto accept iOS user's text replacement + // (e.g. mu->μ) which makes user unable to type "mu". + if !hideCursor { + hideCursor = true + forceResetCursor() } } // MARK: - ThemeApplicable func applyTheme(theme: Theme) { let colors = theme.colors + tintColor = colors.layerSelectedText clearButtonTintColor = colors.iconPrimary - textColor = colors.textPrimary + markedTextStyle = [NSAttributedString.Key.backgroundColor: colors.layerSelectedText] + + if isEditing { + textColor = colors.textPrimary + } + + tintClearButton() } + + // MARK: - Private + @objc + private func textDidChange(_ textField: UITextField) { + // When marked text (autocomplete suggestion) is set this method is called + // in this case we don't need to + guard !isSettingMarkedText else { return } + + hideCursor = markedTextRange != nil + removeCompletion() + + let isKeyboardReplacingText = lastReplacement != nil + if isKeyboardReplacingText, markedTextRange == nil { + notifyTextChanged?() + } else { + hideCursor = false + } + } + + /// Commits the completion by setting the text and removing the highlight. + private func applyCompletion() { + // Clear the current completion, then set the text without the attributed style. + let text = (self.text ?? "") + let didRemoveCompletion = removeCompletion() + self.text = text + hideCursor = false + + // Move the cursor to the end of the completion. + if didRemoveCompletion { + selectedTextRange = textRange(from: endOfDocument, to: endOfDocument) + } + } + + /// Removes the autocomplete-highlighted. Returns true if a completion was actually removed + @objc + @discardableResult + private func removeCompletion() -> Bool { + guard markedTextRange != nil else { return false } + + text = textWithoutSuggestion() + return true + } + + private func textWithoutSuggestion() -> String? { + guard let markedTextRange else { return text } + + let location = offset(from: beginningOfDocument, to: markedTextRange.start) + let length = offset(from: markedTextRange.start, to: markedTextRange.end) + let range = NSRange(location: location, length: length) + return (text as NSString?)?.replacingCharacters(in: range, with: "") + } + + @objc + private func clear() { + text = "" + removeCompletion() + autocompleteDelegate?.locationTextField(self, didEnterText: "") + } + + private func normalizeString(_ string: String) -> String { + return string.lowercased().stringByTrimmingLeadingCharactersInSet(CharacterSet.whitespaces) + } + + // Reset the cursor to the end of the text field. + // This forces `caretRect(for position: UITextPosition)` to be called which will decide if we should show the cursor + // This exists because `caretRect(for position: UITextPosition)` is not called after we apply an autocompletion. + private func forceResetCursor() { + selectedTextRange = nil + selectedTextRange = textRange(from: endOfDocument, to: endOfDocument) + } + + private func tintClearButton() { + // Since we're unable to change the tint color of the clear image, we need to use KVO to + // find the clear button, and tint it ourselves. + // https://stackoverflow.com/questions/27944781/how-to-change-the-tint-color-of-the-clear-button-on-a-uitextfield + guard let image = UIImage(named: StandardImageIdentifiers.Large.crossCircleFill), + let clearButtonTintColor, + let clearButton = value(forKey: "_clearButton") as? UIButton + else { return } + + tintedClearImage = image.withTintColor(clearButtonTintColor) + clearButton.setImage(tintedClearImage, for: []) + } + + // MARK: - UITextFieldDelegate + public func textFieldDidBeginEditing(_ textField: UITextField) { + autocompleteDelegate?.locationTextFieldDidBeginEditing(self) + } + + func textFieldShouldEndEditing(_ textField: UITextField) -> Bool { + applyCompletion() + return true + } + + public func textFieldDidEndEditing(_ textField: UITextField) { + lastReplacement = nil + autocompleteDelegate?.locationTextFieldDidEndEditing(self) + } + + // `shouldChangeCharactersInRange` is called before the text changes, and textDidChange is called after. + // Since the text has changed, remove the completion here, and textDidChange will fire the callback to + // get the new autocompletion. + func textField( + _ textField: UITextField, + shouldChangeCharactersIn range: NSRange, + replacementString string: String + ) -> Bool { + // This happens when you begin typing overtop the old highlighted + // text immediately after focusing the text field. We need to trigger + // a `didEnterText` that looks like a `clear()` so that the SearchLoader + // can reset itself since it will only lookup results if the new text is + // longer than the previous text. + if lastReplacement == nil { + autocompleteDelegate?.locationTextField(self, didEnterText: "") + } + + lastReplacement = string + return true + } + + func textFieldShouldReturn(_ textField: UITextField) -> Bool { + applyCompletion() + return autocompleteDelegate?.locationTextFieldShouldReturn(self) ?? true + } + + func textFieldShouldClear(_ textField: UITextField) -> Bool { + removeCompletion() + return autocompleteDelegate?.locationTextFieldShouldClear(self) ?? true + } + + // MARK: - Debounce + /** + * Taken from http://stackoverflow.com/questions/27116684/how-can-i-debounce-a-method-call + * Allows creating a block that will fire after a delay. Resets the timer if called again before the delay expires. + **/ + private func debounce(_ delay: TimeInterval, action: @escaping () -> Void) -> () -> Void { + let callback = Callback(handler: action) + var timer: Timer? + + return { + // If calling again, invalidate the last timer. + if let timer = timer { + timer.invalidate() + } + timer = Timer( + timeInterval: delay, + target: callback, + selector: #selector(Callback.go), + userInfo: nil, + repeats: false + ) + RunLoop.current.add(timer!, forMode: RunLoop.Mode.default) + } + } } diff --git a/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationView.swift b/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationView.swift index 8bab6ee4947a..6a54a46c04de 100644 --- a/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationView.swift +++ b/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationView.swift @@ -5,7 +5,7 @@ import UIKit import Common -final class LocationView: UIView, UITextFieldDelegate, ThemeApplicable, AccessibilityActionsSource { +final class LocationView: UIView, LocationTextFieldDelegate, ThemeApplicable, AccessibilityActionsSource { // MARK: - Properties private enum UX { static let horizontalSpace: CGFloat = 8 @@ -19,6 +19,7 @@ final class LocationView: UIView, UITextFieldDelegate, ThemeApplicable, Accessib private var searchTerm: String? private var notifyTextChanged: (() -> Void)? private var onTapLockIcon: (() -> Void)? + private var onLongPress: (() -> Void)? private var delegate: LocationViewDelegate? private var isURLTextFieldEmpty: Bool { @@ -42,12 +43,14 @@ final class LocationView: UIView, UITextFieldDelegate, ThemeApplicable, Accessib return CGFloat(width) } + private lazy var urlTextFieldColor: UIColor = .black private lazy var urlTextFieldSubdomainColor: UIColor = .clear private lazy var gradientLayer = CAGradientLayer() private lazy var gradientView: UIView = .build() private var clearButtonWidthConstraint: NSLayoutConstraint? private var urlTextFieldLeadingConstraint: NSLayoutConstraint? + private var lockIconWidthAnchor: NSLayoutConstraint? private lazy var iconContainerStackView: UIStackView = .build { view in view.axis = .horizontal @@ -72,7 +75,7 @@ final class LocationView: UIView, UITextFieldDelegate, ThemeApplicable, Accessib urlTextField.backgroundColor = .clear urlTextField.font = FXFontStyles.Regular.body.scaledFont() urlTextField.adjustsFontForContentSizeCategory = true - urlTextField.delegate = self + urlTextField.autocompleteDelegate = self urlTextField.accessibilityActionsSource = self } @@ -81,6 +84,7 @@ final class LocationView: UIView, UITextFieldDelegate, ThemeApplicable, Accessib super.init(frame: .zero) setupLayout() setupGradientLayer() + addLongPressGestureRecognizer() urlTextField.addTarget(self, action: #selector(LocationView.textDidChange), for: .editingChanged) notifyTextChanged = { [self] in @@ -115,6 +119,11 @@ final class LocationView: UIView, UITextFieldDelegate, ThemeApplicable, Accessib updateIconContainer() self.delegate = delegate searchTerm = state.searchTerm + onLongPress = state.onLongPress + } + + func setAutocompleteSuggestion(_ suggestion: String?) { + urlTextField.setAutocompleteSuggestion(suggestion) } // MARK: - Layout @@ -232,22 +241,45 @@ final class LocationView: UIView, UITextFieldDelegate, ThemeApplicable, Accessib updateGradient() } + private func updateWidthForLockIcon(_ width: CGFloat) { + lockIconWidthAnchor?.isActive = false + lockIconWidthAnchor = lockIconButton.widthAnchor.constraint(equalToConstant: width) + lockIconWidthAnchor?.isActive = true + } + // MARK: - `urlTextField` Configuration private func configureURLTextField(_ state: LocationViewState) { - urlTextField.resignFirstResponder() - urlTextField.text = state.url?.absoluteString + if state.isEditing { + urlTextField.text = (state.searchTerm != nil) ? state.searchTerm : state.url?.absoluteString + } else { + urlTextField.text = state.url?.absoluteString + } + urlTextField.placeholder = state.urlTextFieldPlaceholder - urlAbsolutePath = urlTextField.text + urlAbsolutePath = state.url?.absoluteString + + _ = state.isEditing ? becomeFirstResponder() : resignFirstResponder() + + // Start overlay mode & select text when in edit mode with a search term + if state.isEditing, state.shouldSelectSearchTerm { + DispatchQueue.main.async { + self.urlTextField.selectAll(nil) + } + } } private func formatAndTruncateURLTextField() { + guard !urlTextField.isEditing else { return } + let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineBreakMode = .byTruncatingHead let urlString = urlAbsolutePath ?? "" let (subdomain, normalizedHost) = URL.getSubdomainAndHost(from: urlString) - let attributedString = NSMutableAttributedString(string: normalizedHost) + let attributedString = NSMutableAttributedString( + string: normalizedHost, + attributes: [.foregroundColor: urlTextFieldColor]) if let subdomain { let range = NSRange(location: 0, length: subdomain.count) @@ -266,11 +298,23 @@ final class LocationView: UIView, UITextFieldDelegate, ThemeApplicable, Accessib // MARK: - `lockIconButton` Configuration private func configureLockIconButton(_ state: LocationViewState) { - let lockImage = UIImage(named: state.lockIconImageName)?.withRenderingMode(.alwaysTemplate) + guard let lockIconImageName = state.lockIconImageName else { + updateWidthForLockIcon(0) + return + } + updateWidthForLockIcon(UX.lockIconImageViewSize.width) + + let lockImage = UIImage(named: lockIconImageName)?.withRenderingMode(.alwaysTemplate) lockIconButton.setImage(lockImage, for: .normal) onTapLockIcon = state.onTapLockIcon } + // MARK: - Gesture Recognizers + private func addLongPressGestureRecognizer() { + let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(LocationView.handleLongPress)) + urlTextField.addGestureRecognizer(longPressRecognizer) + } + // MARK: - Selectors @objc func textDidChange(_ textField: UITextField) { @@ -282,20 +326,57 @@ final class LocationView: UIView, UITextFieldDelegate, ThemeApplicable, Accessib onTapLockIcon?() } - // MARK: - UITextFieldDelegate - public func textFieldDidBeginEditing(_ textField: UITextField) { + @objc + private func handleLongPress(_ recognizer: UILongPressGestureRecognizer) { + if recognizer.state == .began { + onLongPress?() + } + } + + // MARK: - LocationTextFieldDelegate + func locationTextField(_ textField: LocationTextField, didEnterText text: String) { + delegate?.locationViewDidEnterText(text) + } + + func locationTextFieldShouldReturn(_ textField: LocationTextField) -> Bool { + guard let text = textField.text else { return true } + if !text.trimmingCharacters(in: .whitespaces).isEmpty { + delegate?.locationViewDidSubmitText(text) + textField.resignFirstResponder() + return true + } else { + return false + } + } + + func locationTextFieldShouldClear(_ textField: LocationTextField) -> Bool { + delegate?.locationViewDidEnterText("") + return true + } + + func locationTextFieldDidCancel(_ textField: LocationTextField) { + delegate?.locationViewDidCancelEditing() + } + + func locationPasteAndGo(_ textField: LocationTextField) { + if let pasteboardContents = UIPasteboard.general.string { + delegate?.locationViewDidSubmitText(pasteboardContents) + } + } + + func locationTextFieldDidBeginEditing(_ textField: UITextField) { updateUIForSearchEngineDisplay() DispatchQueue.main.async { [self] in // `attributedText` property is set to nil to remove all formatting and truncation set before. textField.attributedText = nil textField.text = searchTerm != nil ? searchTerm : urlAbsolutePath - textField.selectAll(nil) } delegate?.locationViewDidBeginEditing(textField.text ?? "") } - public func textFieldDidEndEditing(_ textField: UITextField) { + func locationTextFieldDidEndEditing(_ textField: UITextField) { + formatAndTruncateURLTextField() if isURLTextFieldEmpty { updateGradient() } else { @@ -303,14 +384,6 @@ final class LocationView: UIView, UITextFieldDelegate, ThemeApplicable, Accessib } } - public func textFieldShouldReturn(_ textField: UITextField) -> Bool { - guard let searchText = textField.text?.lowercased(), !searchText.isEmpty else { return false } - - delegate?.locationViewShouldSearchFor(searchText) - textField.resignFirstResponder() - return true - } - // MARK: - Accessibility private func configureA11y(_ state: LocationViewState) { lockIconButton.accessibilityIdentifier = state.lockIconButtonA11yId @@ -333,6 +406,7 @@ final class LocationView: UIView, UITextFieldDelegate, ThemeApplicable, Accessib // MARK: - ThemeApplicable func applyTheme(theme: Theme) { let colors = theme.colors + urlTextFieldColor = colors.textPrimary urlTextFieldSubdomainColor = colors.textSecondary gradientLayer.colors = colors.layerGradientURL.cgColors.reversed() searchEngineImageView.backgroundColor = colors.iconPrimary diff --git a/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationViewDelegate.swift b/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationViewDelegate.swift index efeb791701f2..e78e8cd8354a 100644 --- a/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationViewDelegate.swift +++ b/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationViewDelegate.swift @@ -21,11 +21,14 @@ protocol LocationViewDelegate: AnyObject { /// Called when the location view should perform a search based on the entered text. /// /// - Parameter text: The text for which the location view should search. - func locationViewShouldSearchFor(_ text: String) + func locationViewDidSubmitText(_ text: String) /// Called when requesting custom accessibility actions to be performed on the location view. /// /// - Returns: An optional array of `UIAccessibilityCustomAction` objects. /// Return `nil` if no custom actions are provided. func locationViewAccessibilityActions() -> [UIAccessibilityCustomAction]? + + /// Called when the user cancels entering text into the location view. + func locationViewDidCancelEditing() } diff --git a/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationViewState.swift b/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationViewState.swift index e50985abe37e..2d5a8bf6810c 100644 --- a/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationViewState.swift +++ b/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationViewState.swift @@ -16,10 +16,13 @@ public struct LocationViewState { public let urlTextFieldA11yLabel: String public let searchEngineImage: UIImage? - public let lockIconImageName: String + public let lockIconImageName: String? public let url: URL? public let searchTerm: String? + public let isEditing: Bool + public let shouldSelectSearchTerm: Bool public var onTapLockIcon: (() -> Void)? + public var onLongPress: (() -> Void)? public init( searchEngineImageViewA11yId: String, @@ -30,10 +33,13 @@ public struct LocationViewState { urlTextFieldA11yId: String, urlTextFieldA11yLabel: String, searchEngineImage: UIImage?, - lockIconImageName: String, + lockIconImageName: String?, url: URL?, searchTerm: String?, - onTapLockIcon: (() -> Void)? = nil + isEditing: Bool, + shouldSelectSearchTerm: Bool, + onTapLockIcon: (() -> Void)? = nil, + onLongPress: (() -> Void)? = nil ) { self.searchEngineImageViewA11yId = searchEngineImageViewA11yId self.searchEngineImageViewA11yLabel = searchEngineImageViewA11yLabel @@ -46,6 +52,9 @@ public struct LocationViewState { self.lockIconImageName = lockIconImageName self.url = url self.searchTerm = searchTerm + self.isEditing = isEditing + self.shouldSelectSearchTerm = shouldSelectSearchTerm self.onTapLockIcon = onTapLockIcon + self.onLongPress = onLongPress } } diff --git a/BrowserKit/Sources/ToolbarKit/NavigationToolbar/BrowserNavigationToolbar.swift b/BrowserKit/Sources/ToolbarKit/NavigationToolbar/BrowserNavigationToolbar.swift index 40f2904e29cb..aee530f2c1c7 100644 --- a/BrowserKit/Sources/ToolbarKit/NavigationToolbar/BrowserNavigationToolbar.swift +++ b/BrowserKit/Sources/ToolbarKit/NavigationToolbar/BrowserNavigationToolbar.swift @@ -5,6 +5,10 @@ import UIKit import Common +public protocol BrowserNavigationToolbarDelegate: AnyObject { + func configureContextualHint(for button: UIButton) +} + /// Navigation toolbar implementation. public class BrowserNavigationToolbar: UIView, NavigationToolbar, ThemeApplicable { private enum UX { @@ -13,6 +17,7 @@ public class BrowserNavigationToolbar: UIView, NavigationToolbar, ThemeApplicabl static let borderHeight: CGFloat = 1 } + private weak var toolbarDelegate: BrowserNavigationToolbarDelegate? private lazy var actionStack: UIStackView = .build { view in view.distribution = .equalSpacing } @@ -29,7 +34,9 @@ public class BrowserNavigationToolbar: UIView, NavigationToolbar, ThemeApplicabl fatalError("init(coder:) has not been implemented") } - public func configure(state: NavigationToolbarState) { + public func configure(state: NavigationToolbarState, toolbarDelegate: BrowserNavigationToolbarDelegate) { + self.toolbarDelegate = toolbarDelegate + updateActionStack(toolbarElements: state.actions) // Update border @@ -72,6 +79,10 @@ public class BrowserNavigationToolbar: UIView, NavigationToolbar, ThemeApplicabl // As we recreate the buttons we need to apply the theme for them to be displayed correctly button.applyTheme(theme: theme) } + + if toolbarElement.hasContextualHint == true { + toolbarDelegate?.configureContextualHint(for: button) + } } } @@ -79,6 +90,12 @@ public class BrowserNavigationToolbar: UIView, NavigationToolbar, ThemeApplicabl public func applyTheme(theme: Theme) { backgroundColor = theme.colors.layer1 toolbarBorderView.backgroundColor = theme.colors.borderPrimary + + actionStack.arrangedSubviews.forEach { element in + guard let button = element as? ToolbarButton else { return } + button.applyTheme(theme: theme) + } + self.theme = theme } } diff --git a/BrowserKit/Sources/ToolbarKit/NavigationToolbar/NavigationToolbar.swift b/BrowserKit/Sources/ToolbarKit/NavigationToolbar/NavigationToolbar.swift index b99d6a9f2a21..bfbf8c51cb12 100644 --- a/BrowserKit/Sources/ToolbarKit/NavigationToolbar/NavigationToolbar.swift +++ b/BrowserKit/Sources/ToolbarKit/NavigationToolbar/NavigationToolbar.swift @@ -6,5 +6,5 @@ import Foundation /// Protocol representing an navigation toolbar. public protocol NavigationToolbar { - func configure(state: NavigationToolbarState) + func configure(state: NavigationToolbarState, toolbarDelegate: BrowserNavigationToolbarDelegate) } diff --git a/BrowserKit/Sources/ToolbarKit/ToolbarButton.swift b/BrowserKit/Sources/ToolbarKit/ToolbarButton.swift index 54727b84d23e..a7be39e1fb25 100644 --- a/BrowserKit/Sources/ToolbarKit/ToolbarButton.swift +++ b/BrowserKit/Sources/ToolbarKit/ToolbarButton.swift @@ -14,8 +14,6 @@ class ToolbarButton: UIButton, ThemeApplicable { private struct UX { static let verticalInset: CGFloat = 10 static let horizontalInset: CGFloat = 10 - static let badgeImageViewBorderWidth: CGFloat = 1 - static let badgeImageViewCornerRadius: CGFloat = 10 static let badgeIconSize = CGSize(width: 20, height: 20) } @@ -25,9 +23,10 @@ class ToolbarButton: UIButton, ThemeApplicable { private(set) var backgroundColorNormal: UIColor = .clear private var badgeImageView: UIImageView? - private var shouldDisplayAsHighlighted = false + private var maskImageView: UIImageView? - private var onLongPress: (() -> Void)? + private var shouldDisplayAsHighlighted = false + private var onLongPress: ((UIButton) -> Void)? override init(frame: CGRect) { super.init(frame: frame) @@ -40,14 +39,13 @@ class ToolbarButton: UIButton, ThemeApplicable { } open func configure(element: ToolbarElement) { - guard var config = configuration else { - return - } + guard var config = configuration else { return } removeAllGestureRecognizers() configureLongPressGestureRecognizerIfNeeded(for: element) + configureCustomA11yActionIfNeeded(for: element) shouldDisplayAsHighlighted = element.shouldDisplayAsHighlighted - let image = UIImage(named: element.iconName)?.withRenderingMode(.alwaysTemplate) + let image = imageConfiguredForRTL(for: element) let action = UIAction(title: element.a11yLabel, image: image, handler: { _ in @@ -68,6 +66,9 @@ class ToolbarButton: UIButton, ThemeApplicable { configuration = config if let badgeName = element.badgeImageName { addBadgeIcon(imageName: badgeName) + if let maskImageName = element.maskImageName { + addMaskIcon(maskImageName: maskImageName) + } } layoutIfNeeded() } @@ -77,9 +78,7 @@ class ToolbarButton: UIButton, ThemeApplicable { } override public func updateConfiguration() { - guard var updatedConfiguration = configuration else { - return - } + guard var updatedConfiguration = configuration else { return } switch state { case .highlighted: @@ -99,18 +98,28 @@ class ToolbarButton: UIButton, ThemeApplicable { private func addBadgeIcon(imageName: String) { badgeImageView = UIImageView(image: UIImage(named: imageName)) guard let badgeImageView, configuration?.image != nil else { return } - - badgeImageView.layer.borderWidth = UX.badgeImageViewBorderWidth - badgeImageView.layer.cornerRadius = UX.badgeImageViewCornerRadius - badgeImageView.clipsToBounds = true badgeImageView.translatesAutoresizingMaskIntoConstraints = false imageView?.addSubview(badgeImageView) + applyBadgeConstraints(to: badgeImageView) + } + + private func addMaskIcon(maskImageName: String) { + maskImageView = UIImageView(image: UIImage(named: maskImageName)) + guard let maskImageView, let badgeImageView else { return } + maskImageView.translatesAutoresizingMaskIntoConstraints = false + + maskImageView.addSubview(badgeImageView) + imageView?.addSubview(maskImageView) + applyBadgeConstraints(to: maskImageView) + } + + private func applyBadgeConstraints(to imageView: UIImageView) { NSLayoutConstraint.activate([ - badgeImageView.widthAnchor.constraint(equalToConstant: UX.badgeIconSize.width), - badgeImageView.heightAnchor.constraint(equalToConstant: UX.badgeIconSize.height), - badgeImageView.leadingAnchor.constraint(equalTo: centerXAnchor), - badgeImageView.bottomAnchor.constraint(equalTo: centerYAnchor) + imageView.widthAnchor.constraint(equalToConstant: UX.badgeIconSize.width), + imageView.heightAnchor.constraint(equalToConstant: UX.badgeIconSize.height), + imageView.leadingAnchor.constraint(equalTo: centerXAnchor), + imageView.bottomAnchor.constraint(equalTo: centerYAnchor) ]) } @@ -124,6 +133,21 @@ class ToolbarButton: UIButton, ThemeApplicable { addGestureRecognizer(longPressRecognizer) } + private func configureCustomA11yActionIfNeeded(for element: ToolbarElement) { + guard let a11yCustomActionName = element.a11yCustomActionName, + let a11yCustomAction = element.a11yCustomAction else { return } + let a11yAction = UIAccessibilityCustomAction(name: a11yCustomActionName) { _ in + a11yCustomAction() + return true + } + accessibilityCustomActions = [a11yAction] + } + + private func imageConfiguredForRTL(for element: ToolbarElement) -> UIImage? { + let image = UIImage(named: element.iconName)?.withRenderingMode(.alwaysTemplate) + return element.isFlippedForRTL ? image?.imageFlippedForRightToLeftLayoutDirection() : image + } + private func removeAllGestureRecognizers() { guard let gestureRecognizers else { return } for recognizer in gestureRecognizers { @@ -137,18 +161,23 @@ class ToolbarButton: UIButton, ThemeApplicable { if gestureRecognizer.state == .began { let generator = UIImpactFeedbackGenerator(style: .heavy) generator.impactOccurred() - onLongPress?() + onLongPress?(self) } } // MARK: - ThemeApplicable public func applyTheme(theme: Theme) { - foregroundColorNormal = theme.colors.iconPrimary - foregroundColorHighlighted = theme.colors.actionPrimary - foregroundColorDisabled = theme.colors.iconDisabled - badgeImageView?.layer.borderColor = theme.colors.layer1.cgColor - badgeImageView?.backgroundColor = theme.colors.layer1 + let colors = theme.colors + foregroundColorNormal = colors.iconPrimary + foregroundColorHighlighted = colors.actionPrimary + foregroundColorDisabled = colors.iconDisabled backgroundColorNormal = .clear + + badgeImageView?.layer.borderColor = colors.layer1.cgColor + badgeImageView?.backgroundColor = maskImageView == nil ? colors.layer1 : .clear + badgeImageView?.tintColor = maskImageView == nil ? .clear : colors.actionInfo + maskImageView?.tintColor = colors.layer1 + setNeedsUpdateConfiguration() } } diff --git a/BrowserKit/Sources/ToolbarKit/ToolbarElement.swift b/BrowserKit/Sources/ToolbarKit/ToolbarElement.swift index ed18076f1f4c..853d6eff69e3 100644 --- a/BrowserKit/Sources/ToolbarKit/ToolbarElement.swift +++ b/BrowserKit/Sources/ToolbarKit/ToolbarElement.swift @@ -11,15 +11,24 @@ public struct ToolbarElement: Equatable { /// Badge name of the toolbar element let badgeImageName: String? + /// Mask name of the badge's toolbar element + let maskImageName: String? + /// Number of open tabs let numberOfTabs: Int? /// Whether the toolbar element can be interacted with let isEnabled: Bool + /// Indicates whether the toolbar element's image should be flipped for right-to-left layout direction + let isFlippedForRTL: Bool + /// Indicates if the element should be displayed as highlighted let shouldDisplayAsHighlighted: Bool + /// Indicates that there is an associated contextual hint + let hasContextualHint: Bool? + /// Accessibility label of the toolbar element let a11yLabel: String @@ -29,41 +38,59 @@ public struct ToolbarElement: Equatable { /// Accessibility identifier of the toolbar element let a11yId: String + /// Name for the custom accessibility action + let a11yCustomActionName: String? + + /// Action to be performed for custom accessibility action + let a11yCustomAction: (() -> Void)? + /// Closure that is executed when the toolbar element is tapped let onSelected: ((UIButton) -> Void)? /// Closure that is executed when the toolbar element is long pressed - let onLongPress: (() -> Void)? + let onLongPress: ((UIButton) -> Void)? // We need this init as by default the init generated by the compiler for the struct will be internal and // can therefor not be used outside of the ToolbarKit public init(iconName: String, badgeImageName: String? = nil, + maskImageName: String? = nil, numberOfTabs: Int? = nil, isEnabled: Bool, + isFlippedForRTL: Bool = false, shouldDisplayAsHighlighted: Bool = false, + hasContextualHint: Bool = false, a11yLabel: String, a11yHint: String?, a11yId: String, + a11yCustomActionName: String? = nil, + a11yCustomAction: (() -> Void)? = nil, onSelected: ((UIButton) -> Void)?, - onLongPress: (() -> Void)? = nil) { + onLongPress: ((UIButton) -> Void)? = nil) { self.iconName = iconName self.badgeImageName = badgeImageName + self.maskImageName = maskImageName self.numberOfTabs = numberOfTabs self.isEnabled = isEnabled + self.isFlippedForRTL = isFlippedForRTL self.shouldDisplayAsHighlighted = shouldDisplayAsHighlighted + self.hasContextualHint = hasContextualHint self.onSelected = onSelected self.onLongPress = onLongPress self.a11yLabel = a11yLabel self.a11yHint = a11yHint self.a11yId = a11yId + self.a11yCustomActionName = a11yCustomActionName + self.a11yCustomAction = a11yCustomAction } public static func == (lhs: ToolbarElement, rhs: ToolbarElement) -> Bool { lhs.iconName == rhs.iconName && lhs.badgeImageName == rhs.badgeImageName && + lhs.maskImageName == rhs.maskImageName && lhs.numberOfTabs == rhs.numberOfTabs && lhs.isEnabled == rhs.isEnabled && + lhs.isFlippedForRTL == rhs.isFlippedForRTL && lhs.shouldDisplayAsHighlighted == rhs.shouldDisplayAsHighlighted && lhs.a11yLabel == rhs.a11yLabel && lhs.a11yHint == rhs.a11yHint && diff --git a/BrowserKit/Tests/MenuKitTests/MenuKitTests.swift b/BrowserKit/Tests/MenuKitTests/MenuKitTests.swift new file mode 100644 index 000000000000..060b84dd9ea8 --- /dev/null +++ b/BrowserKit/Tests/MenuKitTests/MenuKitTests.swift @@ -0,0 +1,31 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +@testable import MenuKit + +final class MenuKitTests: XCTestCase { + func testMenuElement_initializesCorrectly() { + let subject = MenuElement( + iconName: "test", + isEnabled: true, + isActive: true, + hasSubmenu: false, + a11yLabel: "test", + a11yHint: nil, + a11yId: "test" + ) + let expectedResult = MenuElement( + iconName: "test", + isEnabled: true, + isActive: true, + hasSubmenu: false, + a11yLabel: "test", + a11yHint: nil, + a11yId: "test" + ) + + XCTAssertEqual(subject, expectedResult) + } +} diff --git a/BrowserKit/Tests/TabDataStoreTests/TabSessionStoreTests.swift b/BrowserKit/Tests/TabDataStoreTests/TabSessionStoreTests.swift index 2967077eb662..c7f23ec9cbc8 100644 --- a/BrowserKit/Tests/TabDataStoreTests/TabSessionStoreTests.swift +++ b/BrowserKit/Tests/TabDataStoreTests/TabSessionStoreTests.swift @@ -21,24 +21,24 @@ final class TabSessionStoreTests: XCTestCase { // MARK: Save - func testSaveWithoutDirectory() async { + func testSaveWithoutDirectory() { let subject = createSubject() let uuid = UUID() let dataFile = Data(count: 100) - await subject.saveTabSession(tabID: uuid, sessionData: dataFile) + subject.saveTabSession(tabID: uuid, sessionData: dataFile) XCTAssertEqual(mockFileManager.tabSessionDataDirectoryCalledCount, 1) XCTAssertEqual(mockFileManager.fileExistsCalledCount, 0) XCTAssertEqual(mockFileManager.createDirectoryAtPathCalledCount, 0) } - func testSaveTabSession() async { + func testSaveTabSession() { let subject = createSubject() let uuid = UUID() let dataFile = Data(count: 100) mockFileManager.primaryDirectoryURL = URL(string: "some/directory") mockFileManager.fileExists = false - await subject.saveTabSession(tabID: uuid, sessionData: dataFile) + subject.saveTabSession(tabID: uuid, sessionData: dataFile) XCTAssertEqual(mockFileManager.tabSessionDataDirectoryCalledCount, 1) XCTAssertEqual(mockFileManager.fileExistsCalledCount, 1) @@ -47,21 +47,21 @@ final class TabSessionStoreTests: XCTestCase { // MARK: Fetch - func testFetchTabSessionWithoutDirectory() async { + func testFetchTabSessionWithoutDirectory() { let subject = createSubject() let uuid = UUID() - _ = await subject.fetchTabSession(tabID: uuid) + _ = subject.fetchTabSession(tabID: uuid) XCTAssertEqual(mockFileManager.tabSessionDataDirectoryCalledCount, 1) } - func testFetchTabSession() async { + func testFetchTabSession() { let subject = createSubject() let uuid = UUID() mockFileManager.primaryDirectoryURL = URL(string: "some/directory") - _ = await subject.fetchTabSession(tabID: uuid) + _ = subject.fetchTabSession(tabID: uuid) XCTAssertEqual(mockFileManager.tabSessionDataDirectoryCalledCount, 1) } diff --git a/BrowserKit/Tests/WebEngineTests/URLExtensionTests.swift b/BrowserKit/Tests/WebEngineTests/URLExtensionTests.swift index 5ee7b65d6567..4d7051c59198 100644 --- a/BrowserKit/Tests/WebEngineTests/URLExtensionTests.swift +++ b/BrowserKit/Tests/WebEngineTests/URLExtensionTests.swift @@ -183,4 +183,18 @@ final class URLExtensionTests: XCTestCase { let safeUrl = url.safeEncodedUrl XCTAssertNil(safeUrl) } + + func testFaviconRootDirectoryURL() { + let url1 = URL(string: "https://some.domain.com/path/subpath") + let favicon1 = url1?.faviconUrl() + XCTAssertEqual(favicon1, URL(string: "https://some.domain.com/favicon.ico")!) + + let url2 = URL(string: "http://website.org////") + let favicon2 = url2?.faviconUrl() + XCTAssertEqual(favicon2, URL(string: "http://website.org/favicon.ico")!) + + let url3 = URL(string: "scheme://another.website.net/path/") + let favicon3 = url3?.faviconUrl() + XCTAssertEqual(favicon3, URL(string: "scheme://another.website.net/favicon.ico")!) + } } diff --git a/SampleBrowser/SampleBrowser.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/SampleBrowser/SampleBrowser.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 0d8d7a515d83..c97fbc9996a9 100644 --- a/SampleBrowser/SampleBrowser.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/SampleBrowser/SampleBrowser.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -41,8 +41,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/getsentry/sentry-cocoa.git", "state" : { - "revision" : "38f4f70d07117b9f958a76b1bff278c2f29ffe0e", - "version" : "8.21.0" + "revision" : "d2ced2d961b34573ebd2ea0567a2f1408e90f0ae", + "version" : "8.34.0" } }, { diff --git a/SampleComponentLibraryApp/SampleComponentLibraryApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/SampleComponentLibraryApp/SampleComponentLibraryApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 0d8d7a515d83..c97fbc9996a9 100644 --- a/SampleComponentLibraryApp/SampleComponentLibraryApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/SampleComponentLibraryApp/SampleComponentLibraryApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -41,8 +41,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/getsentry/sentry-cocoa.git", "state" : { - "revision" : "38f4f70d07117b9f958a76b1bff278c2f29ffe0e", - "version" : "8.21.0" + "revision" : "d2ced2d961b34573ebd2ea0567a2f1408e90f0ae", + "version" : "8.34.0" } }, { diff --git a/SampleComponentLibraryApp/SampleComponentLibraryApp/BottomSheet/BottomSheetComponentViewModel.swift b/SampleComponentLibraryApp/SampleComponentLibraryApp/BottomSheet/BottomSheetComponentViewModel.swift index f13eb00c5b91..d391f1ef173b 100644 --- a/SampleComponentLibraryApp/SampleComponentLibraryApp/BottomSheet/BottomSheetComponentViewModel.swift +++ b/SampleComponentLibraryApp/SampleComponentLibraryApp/BottomSheet/BottomSheetComponentViewModel.swift @@ -12,13 +12,16 @@ struct BottomSheetComponentViewModel: ComponentViewModel { private var viewModel: BottomSheetViewModel init() { - viewModel = BottomSheetViewModel(closeButtonA11yLabel: "Close button", closeButtonA11yIdentifier: "a11yCloseButton") + viewModel = BottomSheetViewModel( + closeButtonA11yLabel: "Close button", + closeButtonA11yIdentifier: "a11yCloseButton") viewModel.shouldDismissForTapOutside = true viewController = BottomSheetViewController( viewModel: viewModel, childViewController: BottomSheetChildViewController(), - usingDimmedBackground: true + usingDimmedBackground: true, + windowUUID: defaultSampleComponentUUID ) } diff --git a/bitrise.yml b/bitrise.yml index 5a32e1618367..c9e8eb65b254 100644 --- a/bitrise.yml +++ b/bitrise.yml @@ -2123,17 +2123,17 @@ app: BITRISE_NIGHTLY_VERSION: '9000' - opts: is_expand: false - BITRISE_RELEASE_VERSION: '130.0' + BITRISE_RELEASE_VERSION: '131.0' - opts: is_expand: false - BITRISE_BETA_VERSION: '130.0' + BITRISE_BETA_VERSION: '131.0' trigger_map: - push_branch: main pipeline: pipeline_build_and_test - push_branch: epic-branch/* pipeline: pipeline_build_and_test -- push_branch: release/v130 +- push_branch: release/v131 pipeline: pipeline_build_and_test - pull_request_target_branch: main pipeline: pipeline_build_and_test diff --git a/firefox-ios/Client.xcodeproj/project.pbxproj b/firefox-ios/Client.xcodeproj/project.pbxproj index a5e2e21666cf..aaf24cb0f202 100644 --- a/firefox-ios/Client.xcodeproj/project.pbxproj +++ b/firefox-ios/Client.xcodeproj/project.pbxproj @@ -15,9 +15,12 @@ 047F9B3224E1FE1F00CD7DF7 /* WidgetKitExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 047F9B2724E1FE1C00CD7DF7 /* WidgetKitExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 047F9B3E24E1FF4000CD7DF7 /* SearchQuickLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 047F9B3A24E1FF4000CD7DF7 /* SearchQuickLinks.swift */; }; 047F9B4224E1FF4000CD7DF7 /* ImageButtonWithLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 047F9B3C24E1FF4000CD7DF7 /* ImageButtonWithLabel.swift */; }; + 0A49784A2C53E63200B1E82A /* TrackingProtectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A4978492C53E63200B1E82A /* TrackingProtectionViewController.swift */; }; 0AC659272BF35854005C614A /* FxAWebViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AC659262BF35854005C614A /* FxAWebViewModelTests.swift */; }; 0AC659292BF493CE005C614A /* MockFxAWebViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AC659282BF493CE005C614A /* MockFxAWebViewModel.swift */; }; 0AD3EEAC2C2485A7001044E5 /* ThemedCenteredTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AD3EEAB2C2485A7001044E5 /* ThemedCenteredTableViewCell.swift */; }; + 0AFF7F642C7784D600265214 /* MockDataCleaner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AFF7F632C7784D600265214 /* MockDataCleaner.swift */; }; + 0AFF7F662C7784F100265214 /* TrackingProtectionModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AFF7F652C7784F000265214 /* TrackingProtectionModelTests.swift */; }; 0B305E1B1E3A98A900BE0767 /* BookmarksTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B305E1A1E3A98A900BE0767 /* BookmarksTests.swift */; }; 0B3D670E1E09B90B00C1EFC7 /* AuthenticationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B3D670D1E09B90B00C1EFC7 /* AuthenticationTest.swift */; }; 0B54BD191B698B7C004C822C /* SuggestedSites.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B54BD181B698B7C004C822C /* SuggestedSites.swift */; }; @@ -361,6 +364,7 @@ 437A857827E43FE100E42764 /* FxAWebViewTelemetry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 437A857727E43FE100E42764 /* FxAWebViewTelemetry.swift */; }; 437A9B682681256800FB41C1 /* LegacyInactiveTabCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 437A9B672681256800FB41C1 /* LegacyInactiveTabCell.swift */; }; 437A9B6A2681257F00FB41C1 /* LegacyInactiveTabViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 437A9B692681257F00FB41C1 /* LegacyInactiveTabViewModel.swift */; }; + 437AE3102C7C9A0C0053F7F3 /* NativeErrorPage.strings in Resources */ = {isa = PBXBuildFile; fileRef = 437AE30E2C7C9A0C0053F7F3 /* NativeErrorPage.strings */; }; 438FE8642988ABA600155B10 /* CreditCardTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 438FE8632988ABA600155B10 /* CreditCardTableViewController.swift */; }; 43903A892C20502A00ACF76E /* ActivityStream.strings in Resources */ = {isa = PBXBuildFile; fileRef = 43903A872C20502A00ACF76E /* ActivityStream.strings */; }; 43903A8C2C20502A00ACF76E /* AddressToolbar.strings in Resources */ = {isa = PBXBuildFile; fileRef = 43903A8A2C20502A00ACF76E /* AddressToolbar.strings */; }; @@ -423,6 +427,7 @@ 43DF945A292258C300590FE3 /* SearchHeaderTitle.strings in Resources */ = {isa = PBXBuildFile; fileRef = 43DF9458292258C300590FE3 /* SearchHeaderTitle.strings */; }; 43E69EC3254D081D00B591C2 /* SimpleTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43E69EAF254D064E00B591C2 /* SimpleTab.swift */; }; 43E69ED7254D081F00B591C2 /* SimpleTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43E69EAF254D064E00B591C2 /* SimpleTab.swift */; }; + 43E6BE162C57AF7600C2152C /* Toolbar.strings in Resources */ = {isa = PBXBuildFile; fileRef = 43E6BE142C57AF7600C2152C /* Toolbar.strings */; }; 43F119002A52E42400C44C6C /* CustomizeFirefoxHome.strings in Resources */ = {isa = PBXBuildFile; fileRef = 43F118FE2A52E42400C44C6C /* CustomizeFirefoxHome.strings */; }; 43F7952525795F69005AEE40 /* SearchTelemetry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43F7952425795F69005AEE40 /* SearchTelemetry.swift */; }; 43F92B3829E9F52B000C0F17 /* AutofillAllFramesAtDocumentStart.js in Resources */ = {isa = PBXBuildFile; fileRef = 43F92B3729E9F52B000C0F17 /* AutofillAllFramesAtDocumentStart.js */; }; @@ -564,6 +569,9 @@ 782B0A362AB41DFC0049EE1A /* FakespotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 782B0A352AB41DFC0049EE1A /* FakespotTests.swift */; }; 787EDD852943EE75002B93AE /* JumpBackInTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 787EDD832943EE75002B93AE /* JumpBackInTests.swift */; }; 78FE1E892B040E7000338465 /* FirefoxSuggestTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78FE1E872B040E7000338465 /* FirefoxSuggestTest.swift */; }; + 7A352B772C4F196B00359D51 /* LegacyTabPeekPreviewActionBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A352B762C4F196B00359D51 /* LegacyTabPeekPreviewActionBuilderTests.swift */; }; + 7A6DF9EB2BFECC3C00A0C608 /* LegacyTabPeekPreviewActionBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6DF9EA2BFECC3C00A0C608 /* LegacyTabPeekPreviewActionBuilder.swift */; }; + 7ADC1D192C27D35B003ED924 /* ActionProviderBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADC1D182C27D35B003ED924 /* ActionProviderBuilder.swift */; }; 7B10AA9F1E3A15020002DD08 /* DataExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B10AA9E1E3A15020002DD08 /* DataExtensions.swift */; }; 7B10AABB1E3A1F650002DD08 /* URLRequestExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B10AABA1E3A1F650002DD08 /* URLRequestExtensions.swift */; }; 7B2142FE1E5E055000CDD3FC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7B2142FC1E5E055000CDD3FC /* InfoPlist.strings */; }; @@ -711,6 +719,7 @@ 8A5604F829DF0D2600035CA3 /* BrowserCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A5604F729DF0D2600035CA3 /* BrowserCoordinatorTests.swift */; }; 8A57519927AD80B800A84DBF /* ReaderModeStyleViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A57519827AD80B800A84DBF /* ReaderModeStyleViewModel.swift */; }; 8A590C6128C123100032F1AA /* OpenPassBookHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A590C6028C123100032F1AA /* OpenPassBookHelper.swift */; }; + 8A5BC1EB2C4AA13F009A40A4 /* FeatureFlagsBoolSetting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A5BC1EA2C4AA13F009A40A4 /* FeatureFlagsBoolSetting.swift */; }; 8A5BD95A28788A3D000FE773 /* TopSitesHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A5BD9582878871B000FE773 /* TopSitesHelperTests.swift */; }; 8A5BD95F2878B7B6000FE773 /* TopSitesWidgetManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A5BD95E2878B7B6000FE773 /* TopSitesWidgetManager.swift */; }; 8A5C3BC5282ABF8E003A8CCF /* LegacyRemoteTabsPanelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A5C3BC4282ABF8E003A8CCF /* LegacyRemoteTabsPanelTests.swift */; }; @@ -797,6 +806,8 @@ 8A95FF642B1E969E00AC303D /* TelemetryContextualIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A95FF632B1E969E00AC303D /* TelemetryContextualIdentifier.swift */; }; 8A95FF672B1E97A800AC303D /* TelemetryContextualIdentifierTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A95FF652B1E977E00AC303D /* TelemetryContextualIdentifierTests.swift */; }; 8A96C4BB28F9E7B300B75884 /* XCTestCaseRootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A96C4BA28F9E7B300B75884 /* XCTestCaseRootViewController.swift */; }; + 8A97E6EA2C58487900F94793 /* AddressLocaleFeatureValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A97E6E92C58487900F94793 /* AddressLocaleFeatureValidator.swift */; }; + 8A97E6EF2C584C4900F94793 /* AddressLocaleFeatureValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A97E6ED2C584AC300F94793 /* AddressLocaleFeatureValidatorTests.swift */; }; 8A9AC465276CEC4E0047F5B0 /* JumpBackInCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A9AC464276CEC4E0047F5B0 /* JumpBackInCell.swift */; }; 8A9AC46B276D11280047F5B0 /* PocketViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A9AC46A276D11280047F5B0 /* PocketViewModel.swift */; }; 8A9B87AD2C1B39100042B894 /* SearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A9B87AC2C1B39100042B894 /* SearchViewModel.swift */; }; @@ -812,6 +823,7 @@ 8AAAB0592C1B7240008830B3 /* MockRustFirefoxSuggest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AAAB0582C1B723F008830B3 /* MockRustFirefoxSuggest.swift */; }; 8AAAB05B2C1B7268008830B3 /* ClientTabsSearchWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AAAB05A2C1B7268008830B3 /* ClientTabsSearchWrapper.swift */; }; 8AAAB05F2C1B72C9008830B3 /* SearchHighlightItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AAAB05E2C1B72C9008830B3 /* SearchHighlightItem.swift */; }; + 8AABB92D2C64F77000F1FE51 /* LoginProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AABB92C2C64F77000F1FE51 /* LoginProvider.swift */; }; 8AABBCFC2A0010900089941E /* GleanWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AABBCFB2A0010900089941E /* GleanWrapper.swift */; }; 8AABBCFF2A0017960089941E /* MockGleanWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AABBCFD2A0017560089941E /* MockGleanWrapper.swift */; }; 8AABBD012A001ADF0089941E /* ApplicationHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AABBD002A001ADF0089941E /* ApplicationHelper.swift */; }; @@ -839,6 +851,10 @@ 8AB8574627D97CB00075C173 /* HomepageContextMenuProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB8574527D97CB00075C173 /* HomepageContextMenuProtocol.swift */; }; 8AB8574827D97CD40075C173 /* HomePanelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB8574727D97CD40075C173 /* HomePanelType.swift */; }; 8AB8574A27D97CE90075C173 /* HomePanelDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB8574927D97CE90075C173 /* HomePanelDelegate.swift */; }; + 8AB893A32C73AF5200DAEED7 /* MockCreditCardProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB893A22C73AF5200DAEED7 /* MockCreditCardProvider.swift */; }; + 8AB893A62C73AFBA00DAEED7 /* MockLoginProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB893A52C73AFBA00DAEED7 /* MockLoginProvider.swift */; }; + 8AB893A72C73AFCC00DAEED7 /* MockLoginViewModelDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A880C432C63CFE200B77F23 /* MockLoginViewModelDelegate.swift */; }; + 8AB893A92C73CBBD00DAEED7 /* CreditCardProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB893A82C73CBBD00DAEED7 /* CreditCardProvider.swift */; }; 8ABA9C8D28931223002C0077 /* MockDispatchQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ABA9C8C28931223002C0077 /* MockDispatchQueue.swift */; }; 8ABA9C8E28931288002C0077 /* JumpBackInDataAdaptorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ABA9C8A28931207002C0077 /* JumpBackInDataAdaptorTests.swift */; }; 8ABC5AEE284532C900FEA552 /* PocketDiscoverCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ABC5AED284532C900FEA552 /* PocketDiscoverCell.swift */; }; @@ -896,6 +912,8 @@ 8AE80BBA2891C0C300BC12EA /* JumpBackInSectionLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AE80BB92891C0C300BC12EA /* JumpBackInSectionLayout.swift */; }; 8AE80BBC2891C20D00BC12EA /* JumpBackInList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AE80BBB2891C20D00BC12EA /* JumpBackInList.swift */; }; 8AE80BBE2891C21A00BC12EA /* JumpBackInSyncedTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AE80BBD2891C21A00BC12EA /* JumpBackInSyncedTab.swift */; }; + 8AEAD9F32C3D7B3E001A2C5A /* FeatureFlagsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AEAD9F22C3D7B3E001A2C5A /* FeatureFlagsSettings.swift */; }; + 8AEAD9F52C3D7BA9001A2C5A /* FeatureFlagsDebugViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AEAD9F42C3D7BA9001A2C5A /* FeatureFlagsDebugViewController.swift */; }; 8AEAD9F92C3DB0CD001A2C5A /* MicrosurveyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AEAD9F62C3DB0BF001A2C5A /* MicrosurveyTests.swift */; }; 8AED23C527AC1F9500DE7E97 /* BaseContentStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AED23C427AC1F9500DE7E97 /* BaseContentStackView.swift */; }; 8AED868328CA3B3400351A50 /* BookmarkPanelViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AED868228CA3B3400351A50 /* BookmarkPanelViewModelTests.swift */; }; @@ -1020,6 +1038,12 @@ AB52ED3B2A0E8873001067F5 /* UserConversionMetrics.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB52ED3A2A0E8873001067F5 /* UserConversionMetrics.swift */; }; AB6FEA202AEA5CA200E7B2F2 /* FakespotAdView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB6FEA1F2AEA5CA200E7B2F2 /* FakespotAdView.swift */; }; AB7D4C3129ACAED100626427 /* Tab+ChangeUserAgentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB7D4C3029ACAED100626427 /* Tab+ChangeUserAgentTests.swift */; }; + AB936A692C05F2B100600F82 /* TrackingProtectionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB936A682C05F2B100600F82 /* TrackingProtectionButton.swift */; }; + AB9CBC022C53B64C00102610 /* TrackingProtectionAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB9CBBFE2C53B64B00102610 /* TrackingProtectionAction.swift */; }; + AB9CBC032C53B64C00102610 /* TrackingProtectionMiddleware.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB9CBBFF2C53B64B00102610 /* TrackingProtectionMiddleware.swift */; }; + AB9CBC042C53B64C00102610 /* TrackingProtectionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB9CBC002C53B64B00102610 /* TrackingProtectionModel.swift */; }; + AB9CBC052C53B64C00102610 /* TrackingProtectionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB9CBC012C53B64B00102610 /* TrackingProtectionState.swift */; }; + AB9CBC082C53B7CD00102610 /* TrackingProtectionStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB9CBC062C53B76400102610 /* TrackingProtectionStateTests.swift */; }; ABB507CF2A136FB2009CAA67 /* UserConversionMetricsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABB507CD2A136FB2009CAA67 /* UserConversionMetricsTests.swift */; }; ABE4393E2AC432040074FFE1 /* PartnerWebsites.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABE4393D2AC432040074FFE1 /* PartnerWebsites.swift */; }; ABEF80D12A24D2BE003F52C4 /* CreditCardBottomSheetViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABEF80D02A24D2BE003F52C4 /* CreditCardBottomSheetViewModel.swift */; }; @@ -1030,6 +1054,7 @@ B12DDFED2A8DE825008CE9CF /* ToolbarMenuTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B12DDFEC2A8DE825008CE9CF /* ToolbarMenuTests.swift */; }; B15058812AA0A878008B7382 /* OpeningScreenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B15058802AA0A878008B7382 /* OpeningScreenTests.swift */; }; B1664E9E2B163B7A005D4C71 /* CreditCardsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1664E9D2B163B7A005D4C71 /* CreditCardsTests.swift */; }; + B1CA62822C0DB43600D31625 /* MultiWindowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1CA62812C0DB43600D31625 /* MultiWindowTests.swift */; }; B1F90EC12BB3F6B600A4D431 /* ZoomingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1F90EC02BB3F6B600A4D431 /* ZoomingTests.swift */; }; B236204B2B851FE1000B1DE7 /* AddressAutoFillBottomSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B236204A2B851FE0000B1DE7 /* AddressAutoFillBottomSheetView.swift */; }; B236204D2B8673DE000B1DE7 /* AddressScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B236204C2B8673DE000B1DE7 /* AddressScrollView.swift */; }; @@ -1501,6 +1526,8 @@ E15DE7C2293A7AED00B32667 /* PhotonActionSheetLineSeparator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E15DE7C1293A7AED00B32667 /* PhotonActionSheetLineSeparator.swift */; }; E15DE7C4293A7B0F00B32667 /* PhotonActionSheetTitleHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E15DE7C3293A7B0F00B32667 /* PhotonActionSheetTitleHeaderView.swift */; }; E16258EF2A83BE0800522742 /* FakespotLoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E16258EE2A83BE0800522742 /* FakespotLoadingView.swift */; }; + E16941B42C4E4A2E00FF5F4E /* BrowserViewControllerStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E16941B32C4E4A2E00FF5F4E /* BrowserViewControllerStateTests.swift */; }; + E16941B82C5119A200FF5F4E /* Autocompletable.swift in Sources */ = {isa = PBXBuildFile; fileRef = E16941B72C5119A200FF5F4E /* Autocompletable.swift */; }; E169C6E82979CA0E0017B8D7 /* URLMailTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E169C6E72979CA0E0017B8D7 /* URLMailTests.swift */; }; E16AD22C2A8A7AE800F0AA58 /* FakespotHighlightsCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E16AD22B2A8A7AE800F0AA58 /* FakespotHighlightsCardView.swift */; }; E16C76812ABDC0DB00172DB5 /* FakespotHighlightsCardViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E16C76802ABDC0DB00172DB5 /* FakespotHighlightsCardViewModelTests.swift */; }; @@ -1511,7 +1538,6 @@ E174963A2992B42C0096900A /* CreditCardSectionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = E17496392992B42C0096900A /* CreditCardSectionHeader.swift */; }; E174963C2992B6A60096900A /* HostingTableViewSectionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = E174963B2992B6A60096900A /* HostingTableViewSectionHeader.swift */; }; E17496402994302D0096900A /* PreferredFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = E174963F2994302D0096900A /* PreferredFont.swift */; }; - E17798962BD6B33300F6F0EB /* ToolbarFlagManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E17798952BD6B33300F6F0EB /* ToolbarFlagManager.swift */; }; E17798982BD6B44B00F6F0EB /* AddressToolbarContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E17798972BD6B44B00F6F0EB /* AddressToolbarContainer.swift */; }; E177989A2BD7D44200F6F0EB /* ToolbarState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E17798992BD7D44200F6F0EB /* ToolbarState.swift */; }; E177989C2BD7D48500F6F0EB /* ToolbarAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = E177989B2BD7D48500F6F0EB /* ToolbarAction.swift */; }; @@ -2129,6 +2155,7 @@ 09D94B5E8CF4C06397168F78 /* my */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = my; path = "my.lproj/Default Browser.strings"; sourceTree = ""; }; 09F14D989587092C60A0078F /* ga */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ga; path = ga.lproj/Shared.strings; sourceTree = ""; }; 0A3A4A9EB784F7903FB13B7A /* ms */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ms; path = ms.lproj/LoginManager.strings; sourceTree = ""; }; + 0A4978492C53E63200B1E82A /* TrackingProtectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackingProtectionViewController.swift; sourceTree = ""; }; 0A574D09BF8D9E37D6C9C654 /* bn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bn; path = bn.lproj/ClearHistoryConfirm.strings; sourceTree = ""; }; 0A5B4CE9B0996AE804491134 /* an */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = an; path = an.lproj/Shared.strings; sourceTree = ""; }; 0A734328A164466314ECE4BE /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/Menu.strings; sourceTree = ""; }; @@ -2137,6 +2164,8 @@ 0AC659282BF493CE005C614A /* MockFxAWebViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockFxAWebViewModel.swift; sourceTree = ""; }; 0AD3EEAB2C2485A7001044E5 /* ThemedCenteredTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemedCenteredTableViewCell.swift; sourceTree = ""; }; 0AE9462E8A8E05CE07D4973D /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoPlist.strings; sourceTree = ""; }; + 0AFF7F632C7784D600265214 /* MockDataCleaner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockDataCleaner.swift; sourceTree = ""; }; + 0AFF7F652C7784F000265214 /* TrackingProtectionModelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrackingProtectionModelTests.swift; sourceTree = ""; }; 0B305E1A1E3A98A900BE0767 /* BookmarksTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarksTests.swift; sourceTree = ""; }; 0B3D670D1E09B90B00C1EFC7 /* AuthenticationTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticationTest.swift; sourceTree = ""; }; 0B414433840C8AA60829B6FB /* ms */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ms; path = ms.lproj/Intro.strings; sourceTree = ""; }; @@ -2812,6 +2841,15 @@ 42CC4F75B4E9E31A4754A9FE /* az */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = az; path = az.lproj/3DTouchActions.strings; sourceTree = ""; }; 42D84A3EBD583A4797682E74 /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/PrivateBrowsing.strings; sourceTree = ""; }; 430057C029E4293100FE51CA /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = "uk.lproj/Edit Card.strings"; sourceTree = ""; }; + 4300B0D92C57B06E00F609C1 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/ActivityStream.strings"; sourceTree = ""; }; + 4300B0DA2C57B06E00F609C1 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/EditAddress.strings"; sourceTree = ""; }; + 4300B0DB2C57B06E00F609C1 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/EnhancedTrackingProtection.strings"; sourceTree = ""; }; + 4300B0DC2C57B06E00F609C1 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/Microsurvey.strings"; sourceTree = ""; }; + 4300B0DD2C57B06E00F609C1 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/ScanQRCode.strings"; sourceTree = ""; }; + 4300B0DE2C57B06E00F609C1 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/SelectCreditCard.strings"; sourceTree = ""; }; + 4300B0DF2C57B06E00F609C1 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/TabLocation.strings"; sourceTree = ""; }; + 4300B0E02C57B06E00F609C1 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/Toolbar.strings"; sourceTree = ""; }; + 4300B0E12C57B06E00F609C1 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/UpdateCard.strings"; sourceTree = ""; }; 4300B70A2C298D9C0099DEEC /* lo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lo; path = lo.lproj/ActivityStream.strings; sourceTree = ""; }; 4300B70B2C298D9C0099DEEC /* lo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lo; path = lo.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; 4300B70C2C298D9C0099DEEC /* lo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lo; path = lo.lproj/LoginsHelper.strings; sourceTree = ""; }; @@ -2914,6 +2952,7 @@ 430405402C298AEC00EB6E76 /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/QRCode.strings; sourceTree = ""; }; 43041686293E0B3100BBFD8A /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Alerts.strings; sourceTree = ""; }; 43041687293E0B3100BBFD8A /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/TabsTray.strings; sourceTree = ""; }; + 430439F42C57B34D005518C3 /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/Toolbar.strings; sourceTree = ""; }; 43045C9729E428A800B5BD9B /* si */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = si; path = si.lproj/Alert.strings; sourceTree = ""; }; 43045C9829E428A800B5BD9B /* si */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = si; path = "si.lproj/Edit Card.strings"; sourceTree = ""; }; 43045C9929E428A800B5BD9B /* si */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = si; path = si.lproj/EditCard.strings; sourceTree = ""; }; @@ -2922,6 +2961,8 @@ 43045C9C29E428A800B5BD9B /* si */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = si; path = si.lproj/ResearchSurface.strings; sourceTree = ""; }; 43045C9D29E428A800B5BD9B /* si */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = si; path = si.lproj/Settings.strings; sourceTree = ""; }; 43045C9E29E428A800B5BD9B /* si */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = si; path = si.lproj/SnackBar.strings; sourceTree = ""; }; + 4304BD432C7C9DCB0063AF05 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/NativeErrorPage.strings; sourceTree = ""; }; + 4304EDF32C60EABA00F3C9DE /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/Toolbar.strings; sourceTree = ""; }; 43050A002ACAE09E002684A4 /* pa-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pa-IN"; path = "pa-IN.lproj/Share.strings"; sourceTree = ""; }; 430514F52AD4195A00188114 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Share.strings; sourceTree = ""; }; 430514F62AD4195A00188114 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/TabLocation.strings; sourceTree = ""; }; @@ -2944,6 +2985,7 @@ 4307A24229F69BC400570352 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/Notification.strings; sourceTree = ""; }; 4307A24329F69BC400570352 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/ZoomPageBar.strings; sourceTree = ""; }; 4308262A2BF506EC002C58F9 /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/Microsurvey.strings; sourceTree = ""; }; + 430832542C7C9E4100F58087 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/NativeErrorPage.strings; sourceTree = ""; }; 43087EE12BF50875005EAA6C /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Microsurvey.strings; sourceTree = ""; }; 4308A4E52A52E38C001D652E /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/SelectCreditCard.strings; sourceTree = ""; }; 430901D52C3C00DB00D5F3AC /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/ActivityStream.strings; sourceTree = ""; }; @@ -2963,9 +3005,11 @@ 430A4A422A0BCCB0009D4128 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Upgrade.strings; sourceTree = ""; }; 430AA4CE29FFD516005AB95C /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Onboarding.strings; sourceTree = ""; }; 430AA4CF29FFD516005AB95C /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/ResearchSurface.strings; sourceTree = ""; }; + 430ADCC62C57B00800B0A55B /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/Toolbar.strings"; sourceTree = ""; }; 430B5C042C20544900652135 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/ActivityStream.strings; sourceTree = ""; }; 430B5C052C20544900652135 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/AddressToolbar.strings; sourceTree = ""; }; 430B5C062C20544900652135 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; + 430B81E02C57B198008C994B /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/Toolbar.strings; sourceTree = ""; }; 430B8F7A2AC1A2B500CDB6CC /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/Share.strings; sourceTree = ""; }; 430B8F7B2AC1A2B500CDB6CC /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/TabLocation.strings; sourceTree = ""; }; 430B9F052A24C0E200DC4B8E /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/DisplayCard.strings; sourceTree = ""; }; @@ -2980,6 +3024,7 @@ 430D1A582B0B7872009067C8 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/FirefoxHomepage.strings; sourceTree = ""; }; 430D39052C3C03D200767610 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/EditAddress.strings; sourceTree = ""; }; 430D39062C3C03D200767610 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/ScanQRCode.strings; sourceTree = ""; }; + 430D6E552C453A7B003ADD09 /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/ScanQRCode.strings; sourceTree = ""; }; 430D8CDE2A124B9700F060A6 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/FirefoxSync.strings; sourceTree = ""; }; 430D8CDF2A124B9700F060A6 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/Upgrade.strings; sourceTree = ""; }; 430D932A29BA69CE00B685EA /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/EngagementNotification.strings; sourceTree = ""; }; @@ -2996,6 +3041,7 @@ 430E7BBF293E0D2B005A10AD /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/TabsTray.strings"; sourceTree = ""; }; 430E91272B5549E300F90C14 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/TabToolbar.strings; sourceTree = ""; }; 430E9CD329225D8300DC418A /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/SearchHeaderTitle.strings; sourceTree = ""; }; + 430EC8982C7C9B5100759193 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/NativeErrorPage.strings; sourceTree = ""; }; 430EE26429BA6968009B5023 /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/EngagementNotification.strings"; sourceTree = ""; }; 430EE26529BA6968009B5023 /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/Onboarding.strings"; sourceTree = ""; }; 430EE26629BA6968009B5023 /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/ResearchSurface.strings"; sourceTree = ""; }; @@ -3065,6 +3111,7 @@ 4314C48E2C2051D0009E4D70 /* hsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hsb; path = hsb.lproj/AddressToolbar.strings; sourceTree = ""; }; 4314C48F2C2051D0009E4D70 /* hsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hsb; path = hsb.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; 4314C4902C2051D0009E4D70 /* hsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hsb; path = hsb.lproj/QRCode.strings; sourceTree = ""; }; + 4315028A2C57B23B005BB18E /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = kk.lproj/Toolbar.strings; sourceTree = ""; }; 4315209A29BA68CF00D729C1 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/EngagementNotification.strings; sourceTree = ""; }; 4315209B29BA68CF00D729C1 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Onboarding.strings; sourceTree = ""; }; 4315209C29BA68CF00D729C1 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/ResearchSurface.strings; sourceTree = ""; }; @@ -3114,6 +3161,7 @@ 43177EC82A49A96800A22B9C /* eo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eo; path = eo.lproj/Upgrade.strings; sourceTree = ""; }; 4317873D2A655B9100A38B47 /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/SelectCreditCard.strings"; sourceTree = ""; }; 4317CE012B0B76F500BD4D5B /* hsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hsb; path = hsb.lproj/FirefoxHomepage.strings; sourceTree = ""; }; + 431813AE2C57B017007C3268 /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/Toolbar.strings"; sourceTree = ""; }; 43183D2F293E0C44009F788D /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Alerts.strings"; sourceTree = ""; }; 43183D30293E0C44009F788D /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/TabsTray.strings"; sourceTree = ""; }; 431853AA2A0911440099B0E0 /* gl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gl; path = "gl.lproj/Default Browser.strings"; sourceTree = ""; }; @@ -3147,6 +3195,12 @@ 431B75792A2DFB2D00D3DD0E /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Footer.strings; sourceTree = ""; }; 431B757A2A2DFB2D00D3DD0E /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/RememberCard.strings; sourceTree = ""; }; 431B757B2A2DFB2D00D3DD0E /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/UpdateCard.strings; sourceTree = ""; }; + 431B95F62C453A89003B17BF /* en-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-CA"; path = "en-CA.lproj/ActivityStream.strings"; sourceTree = ""; }; + 431B95F72C453A89003B17BF /* en-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-CA"; path = "en-CA.lproj/AddressToolbar.strings"; sourceTree = ""; }; + 431B95F82C453A89003B17BF /* en-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-CA"; path = "en-CA.lproj/EditAddress.strings"; sourceTree = ""; }; + 431B95F92C453A89003B17BF /* en-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-CA"; path = "en-CA.lproj/EnhancedTrackingProtection.strings"; sourceTree = ""; }; + 431B95FA2C453A89003B17BF /* en-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-CA"; path = "en-CA.lproj/QRCode.strings"; sourceTree = ""; }; + 431B95FB2C453A8A003B17BF /* en-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-CA"; path = "en-CA.lproj/ScanQRCode.strings"; sourceTree = ""; }; 431BAAF7293E0D3500F58007 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/Alerts.strings"; sourceTree = ""; }; 431BAAF8293E0D3500F58007 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/TabsTray.strings"; sourceTree = ""; }; 431BBDE72ACADE7E003BF226 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Share.strings; sourceTree = ""; }; @@ -3175,6 +3229,8 @@ 431D38832A49AADF00A8BE49 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/UpdateCard.strings; sourceTree = ""; }; 431D44862AEFC97A00B92311 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Shopping.strings; sourceTree = ""; }; 431D65DB292B94F800BB9A90 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/SearchHeaderTitle.strings; sourceTree = ""; }; + 431D82792C57B3F600062D71 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Toolbar.strings; sourceTree = ""; }; + 431D8FEE2C7C9B61001E041F /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/NativeErrorPage.strings; sourceTree = ""; }; 431DACF12A24BF0C00D7CE85 /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/DisplayCard.strings"; sourceTree = ""; }; 431DE38F2B023BC600BF06E3 /* eo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eo; path = eo.lproj/Shopping.strings; sourceTree = ""; }; 431DF6C52C20532100ADD05A /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/ActivityStream.strings; sourceTree = ""; }; @@ -3219,6 +3275,8 @@ 4320E17E2A16E5A9009A4B5F /* CreditCardExtras.ios.mjs */ = {isa = PBXFileReference; lastKnownFileType = text; path = CreditCardExtras.ios.mjs; sourceTree = ""; }; 43211DCD2C3C045A00E4CA4D /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/EditAddress.strings; sourceTree = ""; }; 43211DCE2C3C045A00E4CA4D /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/ScanQRCode.strings; sourceTree = ""; }; + 43211E6B2C57B11F003F9FAF /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/Toolbar.strings; sourceTree = ""; }; + 4321F0522C7C9BE00001D0DD /* hsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hsb; path = hsb.lproj/NativeErrorPage.strings; sourceTree = ""; }; 4321FAB82B8CA9A70046DCA0 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/KeyboardAccessory.strings; sourceTree = ""; }; 4321FAB92B8CA9A70046DCA0 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/PasswordAutofill.strings; sourceTree = ""; }; 4322680229225CA3008F8C47 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/SearchHeaderTitle.strings; sourceTree = ""; }; @@ -3305,6 +3363,8 @@ 4328FAD229D1B35100DABB9A /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/SnackBar.strings; sourceTree = ""; }; 4329591628D8852300D8BC68 /* eo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eo; path = eo.lproj/JumpBackIn.strings; sourceTree = ""; }; 4329591728D8852400D8BC68 /* eo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eo; path = eo.lproj/ToolbarLocation.strings; sourceTree = ""; }; + 432985492C57B47B00FE7CA2 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Toolbar.strings; sourceTree = ""; }; + 4329BDC32C57B1BF007F18C7 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/Toolbar.strings; sourceTree = ""; }; 432A14A0292B93900059D21F /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/SearchHeaderTitle.strings; sourceTree = ""; }; 432A50F52B14B165003EE621 /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = kab.lproj/FirefoxHomepage.strings; sourceTree = ""; }; 432A91F328B39EAE006CE202 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/JumpBackIn.strings"; sourceTree = ""; }; @@ -3372,6 +3432,8 @@ 432FFF5B2C2051FC00ADC8E7 /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/AddressToolbar.strings; sourceTree = ""; }; 432FFF5C2C2051FC00ADC8E7 /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; 432FFF5D2C2051FC00ADC8E7 /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/QRCode.strings; sourceTree = ""; }; + 433020F72C57B40600AF5EE2 /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/Credentials.strings"; sourceTree = ""; }; + 433020F82C57B40700AF5EE2 /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/EditAddress.strings"; sourceTree = ""; }; 4330773429F69CC7006BC636 /* pa-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pa-IN"; path = "pa-IN.lproj/Notification.strings"; sourceTree = ""; }; 4330773529F69CC7006BC636 /* pa-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pa-IN"; path = "pa-IN.lproj/ZoomPageBar.strings"; sourceTree = ""; }; 4330958B2B83917C00FAD61B /* hsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hsb; path = hsb.lproj/KeyboardAccessory.strings; sourceTree = ""; }; @@ -3405,6 +3467,7 @@ 433204162B68010A00ECE7AC /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/TabToolbar.strings; sourceTree = ""; }; 4332247E2A5C203D00B07818 /* pa-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pa-IN"; path = "pa-IN.lproj/CustomizeFirefoxHome.strings"; sourceTree = ""; }; 4332247F2A5C203D00B07818 /* pa-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pa-IN"; path = "pa-IN.lproj/SelectCreditCard.strings"; sourceTree = ""; }; + 43328A002C57B38800444F7E /* pa-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pa-IN"; path = "pa-IN.lproj/Toolbar.strings"; sourceTree = ""; }; 4332AE0B2B305F6200885C18 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/ContextualHints.strings; sourceTree = ""; }; 4332AE0C2B305F6200885C18 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/CredentialProvider.strings; sourceTree = ""; }; 4332AE0D2B305F6200885C18 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Credentials.strings; sourceTree = ""; }; @@ -3429,6 +3492,7 @@ 433429FC29BA6CB1005B05B0 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/ResearchSurface.strings"; sourceTree = ""; }; 43349476294745560027B85C /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sq; path = sq.lproj/Alerts.strings; sourceTree = ""; }; 43349477294745560027B85C /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sq; path = sq.lproj/TabsTray.strings; sourceTree = ""; }; + 4334C4F02C7C9EDE00FBEFCF /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/NativeErrorPage.strings; sourceTree = ""; }; 4334CA5C2BB195DE00B29D5B /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/BottomSheet.strings; sourceTree = ""; }; 4334DC2B29225A0B0064F9CD /* es-AR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-AR"; path = "es-AR.lproj/SearchHeaderTitle.strings"; sourceTree = ""; }; 4335A79E2B554A630087D279 /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/TabToolbar.strings; sourceTree = ""; }; @@ -3459,14 +3523,20 @@ 4337C9D62A5C211300987DEB /* tt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tt; path = tt.lproj/SelectCreditCard.strings; sourceTree = ""; }; 4337C9D72A5C211300987DEB /* tt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tt; path = tt.lproj/UpdateCard.strings; sourceTree = ""; }; 4337E1142AEFC993007579AC /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/Shopping.strings"; sourceTree = ""; }; + 43383DE92C57AF94001BCD0E /* co */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = co; path = co.lproj/EditAddress.strings; sourceTree = ""; }; + 43383DEA2C57AF94001BCD0E /* co */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = co; path = co.lproj/ScanQRCode.strings; sourceTree = ""; }; + 43383DEB2C57AF94001BCD0E /* co */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = co; path = co.lproj/Toolbar.strings; sourceTree = ""; }; 4338633128CF4C5100564CA4 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/JumpBackIn.strings; sourceTree = ""; }; 4338633228CF4C5100564CA4 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/ToolbarLocation.strings; sourceTree = ""; }; + 4338F50C2C453D8800851075 /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/EditAddress.strings; sourceTree = ""; }; + 4338F50D2C453D8800851075 /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/ScanQRCode.strings; sourceTree = ""; }; 4339307829BA698C001A4CAB /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/EngagementNotification.strings"; sourceTree = ""; }; 4339307929BA698C001A4CAB /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/Onboarding.strings"; sourceTree = ""; }; 4339307A29BA698C001A4CAB /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/ResearchSurface.strings"; sourceTree = ""; }; 4339390B28B39F8F001B8301 /* fil */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fil; path = fil.lproj/BookmarkPanel.strings; sourceTree = ""; }; 433942802AC1A24E009B3F3C /* gl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gl; path = gl.lproj/Share.strings; sourceTree = ""; }; 433942812AC1A24E009B3F3C /* gl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gl; path = gl.lproj/TabLocation.strings; sourceTree = ""; }; + 433954F62C57B0A900EC1EAE /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Toolbar.strings; sourceTree = ""; }; 4339D0B32BA85A7400521234 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/BottomSheet.strings"; sourceTree = ""; }; 4339E2A02A49A9250071F5CF /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Footer.strings; sourceTree = ""; }; 4339E2A12A49A9250071F5CF /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/RememberCard.strings; sourceTree = ""; }; @@ -3511,6 +3581,8 @@ 433D51742C3C0478005D19E3 /* ug */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ug; path = ug.lproj/EditAddress.strings; sourceTree = ""; }; 433D51752C3C0478005D19E3 /* ug */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ug; path = ug.lproj/ScanQRCode.strings; sourceTree = ""; }; 433D53CB2B0B798700F5786C /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/FirefoxHomepage.strings; sourceTree = ""; }; + 433D64C62C60EE1100CEC389 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/ScanQRCode.strings; sourceTree = ""; }; + 433D64C72C60EE1100CEC389 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Toolbar.strings; sourceTree = ""; }; 433DD5FB2AEFC6EB00C80D13 /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/Shopping.strings; sourceTree = ""; }; 433E0DEC26FCE37C00B78CC5 /* ne-NP */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "ne-NP"; path = "ne-NP.lproj/Default Browser.strings"; sourceTree = ""; }; 433E11402A124A2C00B4C650 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/FirefoxSync.strings; sourceTree = ""; }; @@ -3532,6 +3604,7 @@ 433FD5CD2A52E55400E7B4CB /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/SelectCreditCard.strings; sourceTree = ""; }; 433FF82028BCD74B006A70C8 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/JumpBackIn.strings; sourceTree = ""; }; 433FF82128BCD74B006A70C8 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/ToolbarLocation.strings; sourceTree = ""; }; + 43400BC22C7C9C7100D8ECE9 /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = kab.lproj/NativeErrorPage.strings; sourceTree = ""; }; 434073A929E4271900458D21 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = "hu.lproj/Edit Card.strings"; sourceTree = ""; }; 434094D1293E0B6000CAD2F5 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Alerts.strings; sourceTree = ""; }; 434094D2293E0B6000CAD2F5 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/TabsTray.strings; sourceTree = ""; }; @@ -3578,6 +3651,7 @@ 4344D02F292259CE00B12BF8 /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/SearchHeaderTitle.strings"; sourceTree = ""; }; 4345055729E4289200F137B6 /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/Settings.strings"; sourceTree = ""; }; 4345441C26D2E52600D5EEAA /* SearchTermGroupsUtility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTermGroupsUtility.swift; sourceTree = ""; }; + 4345E88F2C7C9A5B00FC0D7D /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/NativeErrorPage.strings; sourceTree = ""; }; 43464A7329E4264400C3CA87 /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/Edit Card.strings"; sourceTree = ""; }; 4346FF07295BA6A200F4D220 /* CreditCardSettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreditCardSettingsViewController.swift; sourceTree = ""; }; 4347022C2AC1A19200BEB809 /* dsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = dsb; path = dsb.lproj/Share.strings; sourceTree = ""; }; @@ -3647,12 +3721,15 @@ 434E1FDD299A549B00F79A7B /* fil */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fil; path = fil.lproj/Localizable.strings; sourceTree = ""; }; 434E733625EED32E006D3BDE /* BrowserViewController+URLBarDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BrowserViewController+URLBarDelegate.swift"; sourceTree = ""; }; 434EA84429225CDE0039FBE1 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/SearchHeaderTitle.strings; sourceTree = ""; }; + 434ED7EC2C57B1D4000C86E4 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Toolbar.strings; sourceTree = ""; }; 434EE0B429225A31005D030A /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/SearchHeaderTitle.strings"; sourceTree = ""; }; 434F05982A0BCD36008B1057 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/FirefoxSync.strings; sourceTree = ""; }; 434F05992A0BCD36008B1057 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Upgrade.strings; sourceTree = ""; }; 434F78E429225CD10037862C /* si */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = si; path = si.lproj/SearchHeaderTitle.strings; sourceTree = ""; }; + 434FE6142C7C9C7F00C44560 /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = kk.lproj/NativeErrorPage.strings; sourceTree = ""; }; 434FFA2E2AEFC8EE008CAD12 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/Shopping.strings; sourceTree = ""; }; 434FFF292ACAE1C000680A93 /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/Share.strings"; sourceTree = ""; }; + 43500A6B2C7C9EFB008E385F /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/NativeErrorPage.strings"; sourceTree = ""; }; 43509CB3291BF65400DF631E /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/JumpBackIn.strings; sourceTree = ""; }; 43509CB4291BF65400DF631E /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/ToolbarLocation.strings; sourceTree = ""; }; 4350E75F2AC1A19C004BA97D /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/Share.strings; sourceTree = ""; }; @@ -3687,9 +3764,11 @@ 4353DE762AC1A267005E189E /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/TabLocation.strings; sourceTree = ""; }; 4354415E29EF0682004EDDAF /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = "eu.lproj/Edit Card.strings"; sourceTree = ""; }; 4354485A2B0B767600F22F17 /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/FirefoxHomepage.strings; sourceTree = ""; }; + 43548AB92C7C9A6A0004C846 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/NativeErrorPage.strings; sourceTree = ""; }; 43548D222A2DFAB400E28766 /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = kk.lproj/Footer.strings; sourceTree = ""; }; 43548D232A2DFAB400E28766 /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = kk.lproj/RememberCard.strings; sourceTree = ""; }; 43548D242A2DFAB400E28766 /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = kk.lproj/UpdateCard.strings; sourceTree = ""; }; + 435538172C453B3900DD79B0 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/ScanQRCode.strings; sourceTree = ""; }; 43553E912C298AA30068B484 /* br */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = br; path = br.lproj/ActivityStream.strings; sourceTree = ""; }; 43553E922C298AA30068B484 /* br */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = br; path = br.lproj/AddressToolbar.strings; sourceTree = ""; }; 43553E932C298AA30068B484 /* br */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = br; path = br.lproj/BottomSheet.strings; sourceTree = ""; }; @@ -3719,6 +3798,7 @@ 43569A322A0BCB0E00BCDA77 /* es-AR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-AR"; path = "es-AR.lproj/Upgrade.strings"; sourceTree = ""; }; 4356A5C82BE8F2C300A39580 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Microsurvey.strings; sourceTree = ""; }; 4356DB4F2AEFC6FF00ADF289 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Shopping.strings; sourceTree = ""; }; + 43571C312C57B52200A2DD89 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Toolbar.strings; sourceTree = ""; }; 43571D57293E0A2000EF42C5 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Alerts.strings; sourceTree = ""; }; 43571D58293E0A2000EF42C5 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/TabsTray.strings; sourceTree = ""; }; 435784DC2A24C0C80046746E /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/DisplayCard.strings"; sourceTree = ""; }; @@ -3749,6 +3829,7 @@ 4358DEF82A0BCAED000A7CED /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/Upgrade.strings"; sourceTree = ""; }; 4358FE5C29F69A790057F216 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Notification.strings; sourceTree = ""; }; 4358FE5D29F69A790057F216 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/ZoomPageBar.strings; sourceTree = ""; }; + 43590D282C453A350085A1D2 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/EditAddress.strings; sourceTree = ""; }; 4359303C29F69CDF00896524 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Notification.strings"; sourceTree = ""; }; 4359303D29F69CDF00896524 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/ZoomPageBar.strings"; sourceTree = ""; }; 435956AF2A24BF3B0029BB1D /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/DisplayCard.strings; sourceTree = ""; }; @@ -3762,6 +3843,8 @@ 435A759129E4285900778D0C /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Edit Card.strings"; sourceTree = ""; }; 435AC01A2A52E65800B0156A /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/CustomizeFirefoxHome.strings; sourceTree = ""; }; 435AC01B2A52E65800B0156A /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/SelectCreditCard.strings; sourceTree = ""; }; + 435B35652C57AFA200F1C0BE /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/ScanQRCode.strings; sourceTree = ""; }; + 435B35662C57AFA200F1C0BE /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Toolbar.strings; sourceTree = ""; }; 435B625129D1B3D2005FBA5D /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Alert.strings; sourceTree = ""; }; 435B625229D1B3D2005FBA5D /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/BiometricAuthentication.strings; sourceTree = ""; }; 435B625329D1B3D2005FBA5D /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/EditCard.strings; sourceTree = ""; }; @@ -3858,6 +3941,10 @@ 4361333D2A091002009C8BBC /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Notification.strings; sourceTree = ""; }; 4361333E2A091002009C8BBC /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Settings.strings; sourceTree = ""; }; 4361333F2A091002009C8BBC /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/SnackBar.strings; sourceTree = ""; }; + 4361916A2C7C9D72005B31E3 /* pa-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pa-IN"; path = "pa-IN.lproj/NativeErrorPage.strings"; sourceTree = ""; }; + 4361DF292C453DB200CE1295 /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/BottomSheet.strings"; sourceTree = ""; }; + 4361DF2A2C453DB200CE1295 /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/ContextualHints.strings"; sourceTree = ""; }; + 4361DF2B2C453DB200CE1295 /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/CredentialProvider.strings"; sourceTree = ""; }; 43620CB22BA85B0E00F9248C /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/BottomSheet.strings; sourceTree = ""; }; 43621A6E29E428B300B2ED64 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = "sk.lproj/Edit Card.strings"; sourceTree = ""; }; 436220A028F436A700C8E75B /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/JumpBackIn.strings"; sourceTree = ""; }; @@ -3872,6 +3959,7 @@ 4363AC042A0BCE1D00A18A25 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/Upgrade.strings"; sourceTree = ""; }; 4363B6662BE8F3C2004D0CED /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = kk.lproj/Microsurvey.strings; sourceTree = ""; }; 4363FDF12AEFC70A00FC80F2 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Shopping.strings; sourceTree = ""; }; + 4364018E2C7C9AE700FF64C6 /* es-AR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-AR"; path = "es-AR.lproj/NativeErrorPage.strings"; sourceTree = ""; }; 436403C428EAFBD6008A7F9B /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = kk.lproj/JumpBackIn.strings; sourceTree = ""; }; 436403C528EAFBD6008A7F9B /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = kk.lproj/ToolbarLocation.strings; sourceTree = ""; }; 43645E3629BA68D900CE6033 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/EngagementNotification.strings; sourceTree = ""; }; @@ -3931,6 +4019,7 @@ 4366A0B429DAEDF500DA8329 /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/ResearchSurface.strings; sourceTree = ""; }; 4366A0B529DAEDF500DA8329 /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/Settings.strings; sourceTree = ""; }; 4366A0B629DAEDF500DA8329 /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/SnackBar.strings; sourceTree = ""; }; + 436705DA2C57B3D7002C7FE6 /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/Toolbar.strings; sourceTree = ""; }; 43680DD7293E0A160013269F /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/Alerts.strings; sourceTree = ""; }; 43680DD8293E0A160013269F /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/TabsTray.strings; sourceTree = ""; }; 43682B4D28B39CFF00F2C865 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/JumpBackIn.strings; sourceTree = ""; }; @@ -3950,6 +4039,7 @@ 4368ED3A28B39C1300A50B64 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/JumpBackIn.strings"; sourceTree = ""; }; 4368ED3B28B39C1300A50B64 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/ToolbarLocation.strings"; sourceTree = ""; }; 4368EE5B292B95C0005A0673 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/SearchHeaderTitle.strings; sourceTree = ""; }; + 436957A82C57AFB100B079FE /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/ScanQRCode.strings; sourceTree = ""; }; 43696A66292B9719002C3EAD /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/SearchHeaderTitle.strings"; sourceTree = ""; }; 436985E428B39FFE00198055 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/JumpBackIn.strings"; sourceTree = ""; }; 436985E528B39FFE00198055 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/ToolbarLocation.strings"; sourceTree = ""; }; @@ -4036,6 +4126,7 @@ 436F42FD2A52E6E100E4C2A2 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/SelectCreditCard.strings; sourceTree = ""; }; 436F68302AE68E3700BF46C9 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Share.strings; sourceTree = ""; }; 436F84692A4070C800279DAE /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/Onboarding.strings; sourceTree = ""; }; + 436FC4912C7C9D810083D324 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/NativeErrorPage.strings; sourceTree = ""; }; 436FC6A82A3734F30010789D /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Footer.strings; sourceTree = ""; }; 436FC6A92A3734F30010789D /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/RememberCard.strings; sourceTree = ""; }; 436FC6AA2A3734F40010789D /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/UpdateCard.strings; sourceTree = ""; }; @@ -4073,6 +4164,7 @@ 437327D62B305F6E0090FCBC /* dsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = dsb; path = dsb.lproj/FirefoxLogins.strings; sourceTree = ""; }; 437327D72B305F6E0090FCBC /* dsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = dsb; path = dsb.lproj/LoginsHelper.strings; sourceTree = ""; }; 437330442B554BE600BB1AFC /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/TabToolbar.strings"; sourceTree = ""; }; + 4373440E2C7C9AF500457D4D /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/NativeErrorPage.strings"; sourceTree = ""; }; 43736FDD2A406F2900546F4A /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/Footer.strings; sourceTree = ""; }; 43736FDE2A406F2900546F4A /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/RememberCard.strings; sourceTree = ""; }; 43736FDF2A406F2900546F4A /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/UpdateCard.strings; sourceTree = ""; }; @@ -4095,10 +4187,17 @@ 4374AB3C2C205468004C030B /* su */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = su; path = su.lproj/QRCode.strings; sourceTree = ""; }; 4374AC0128B39AE700BE4998 /* bo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bo; path = bo.lproj/BookmarkPanel.strings; sourceTree = ""; }; 4374C6AF2AEFC75A00907BB4 /* hsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hsb; path = hsb.lproj/Shopping.strings; sourceTree = ""; }; + 437511122C60EA6E001E649A /* br */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = br; path = br.lproj/Toolbar.strings; sourceTree = ""; }; 437579372B0B773B005F2178 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/FirefoxHomepage.strings; sourceTree = ""; }; 437582642BE8F37A006F43C6 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Microsurvey.strings; sourceTree = ""; }; 43758DC32B0B78E300DA928C /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/FirefoxHomepage.strings; sourceTree = ""; }; 4375C3222A24BF03004E1E5A /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/DisplayCard.strings"; sourceTree = ""; }; + 4375F0E12C453BC4001F3A55 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/ActivityStream.strings; sourceTree = ""; }; + 4375F0E22C453BC4001F3A55 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/AddressToolbar.strings; sourceTree = ""; }; + 4375F0E32C453BC4001F3A55 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/EditAddress.strings; sourceTree = ""; }; + 4375F0E42C453BC4001F3A55 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; + 4375F0E52C453BC4001F3A55 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/QRCode.strings; sourceTree = ""; }; + 4375F0E62C453BC4001F3A55 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/ScanQRCode.strings; sourceTree = ""; }; 4375F7DC2A124BFB002484E5 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/FirefoxSync.strings; sourceTree = ""; }; 4375F7DD2A124BFB002484E5 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Upgrade.strings; sourceTree = ""; }; 437668872BE8F52600C19A1B /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/Microsurvey.strings; sourceTree = ""; }; @@ -4130,6 +4229,9 @@ 4378BD402B14B2C5004CB18D /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/FirefoxHomepage.strings; sourceTree = ""; }; 4378C733293E09C200D0C1C7 /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/Alerts.strings; sourceTree = ""; }; 4378C736293E09C200D0C1C7 /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/TabsTray.strings; sourceTree = ""; }; + 4378EFA72C57AFBF0031897A /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/EditAddress.strings; sourceTree = ""; }; + 4378EFA82C57AFBF0031897A /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/ScanQRCode.strings; sourceTree = ""; }; + 4378EFA92C57AFBF0031897A /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Toolbar.strings; sourceTree = ""; }; 4378F1B329BA6AD200D37BDC /* lo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lo; path = lo.lproj/EngagementNotification.strings; sourceTree = ""; }; 4378F1B429BA6AD200D37BDC /* lo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lo; path = lo.lproj/Onboarding.strings; sourceTree = ""; }; 4378F1B529BA6AD200D37BDC /* lo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lo; path = lo.lproj/ResearchSurface.strings; sourceTree = ""; }; @@ -4141,6 +4243,7 @@ 43790BF4292B94C500968A85 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/SearchHeaderTitle.strings; sourceTree = ""; }; 4379F313293E0A5A0029C76D /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/Alerts.strings"; sourceTree = ""; }; 4379F314293E0A5A0029C76D /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/TabsTray.strings"; sourceTree = ""; }; + 437A0B5A2C57B22500FCF751 /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = kab.lproj/Toolbar.strings; sourceTree = ""; }; 437A857727E43FE100E42764 /* FxAWebViewTelemetry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FxAWebViewTelemetry.swift; sourceTree = ""; }; 437A88922C2054C100031657 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; 437A88932C2054C100031657 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/QRCode.strings; sourceTree = ""; }; @@ -4155,6 +4258,7 @@ 437AA7E72C32C753001A7826 /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = kab.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; 437ABC202B8393050027444C /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/KeyboardAccessory.strings"; sourceTree = ""; }; 437ABC212B8393050027444C /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/PasswordAutofill.strings"; sourceTree = ""; }; + 437AE30F2C7C9A0C0053F7F3 /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/NativeErrorPage.strings; sourceTree = ""; }; 437B0B2C2A6E941500DD9F66 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/Alert.strings; sourceTree = ""; }; 437B0B2D2A6E941500DD9F66 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/Alerts.strings; sourceTree = ""; }; 437B0B2E2A6E941500DD9F66 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/CustomizeFirefoxHome.strings; sourceTree = ""; }; @@ -4195,6 +4299,7 @@ 437CC8E02BE8F1D100CB4385 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Microsurvey.strings; sourceTree = ""; }; 437CCDC82A124D7000A10106 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/FirefoxSync.strings; sourceTree = ""; }; 437CCDC92A124D7000A10106 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Upgrade.strings; sourceTree = ""; }; + 437CD9FF2C7C9A8B00A34E3E /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/NativeErrorPage.strings; sourceTree = ""; }; 437CFF60293E0AD6001F1948 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Alerts.strings; sourceTree = ""; }; 437CFF61293E0AD6001F1948 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/TabsTray.strings; sourceTree = ""; }; 437D59F62AC1A28E00D93351 /* hsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hsb; path = hsb.lproj/Share.strings; sourceTree = ""; }; @@ -4209,9 +4314,17 @@ 437DFCD12AEFC7B500F701AE /* ka */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ka; path = ka.lproj/Shopping.strings; sourceTree = ""; }; 437E193A29F69ABA00F743E0 /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/Notification.strings"; sourceTree = ""; }; 437E193B29F69ABB00F743E0 /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/ZoomPageBar.strings"; sourceTree = ""; }; + 437E27022C7C9D8F00CE7490 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/NativeErrorPage.strings"; sourceTree = ""; }; 437E34352C3C03870025BFA9 /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/ResearchSurface.strings; sourceTree = ""; }; 437E6394293E0BF6008C190A /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Alerts.strings; sourceTree = ""; }; 437E6395293E0BF6008C190A /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/TabsTray.strings; sourceTree = ""; }; + 437E67412C57B4AB00A8791A /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/ActivityStream.strings; sourceTree = ""; }; + 437E67422C57B4AB00A8791A /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/AddressToolbar.strings; sourceTree = ""; }; + 437E67432C57B4AB00A8791A /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/EditAddress.strings; sourceTree = ""; }; + 437E67442C57B4AB00A8791A /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; + 437E67452C57B4AB00A8791A /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/QRCode.strings; sourceTree = ""; }; + 437E67462C57B4AB00A8791A /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/ScanQRCode.strings; sourceTree = ""; }; + 437E67472C57B4AB00A8791A /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/Toolbar.strings; sourceTree = ""; }; 437EA6282B83915700DE9E71 /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/KeyboardAccessory.strings; sourceTree = ""; }; 437EA6292B83915700DE9E71 /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/PasswordAutofill.strings; sourceTree = ""; }; 437F339929EF08FC003E82BC /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = "rm.lproj/Edit Card.strings"; sourceTree = ""; }; @@ -4262,6 +4375,7 @@ 4383320A29BF3D4000DCE236 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Onboarding.strings; sourceTree = ""; }; 4383320B29BF3D4000DCE236 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/ResearchSurface.strings; sourceTree = ""; }; 43838CCA28B39DB500238A7F /* kn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kn; path = kn.lproj/BookmarkPanel.strings; sourceTree = ""; }; + 4383E28B2C453EAA00B775A3 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/ScanQRCode.strings; sourceTree = ""; }; 4384310A2A124D2900E52121 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/FirefoxSync.strings; sourceTree = ""; }; 4384310B2A124D2900E52121 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Upgrade.strings; sourceTree = ""; }; 4384513F28C6127B0043E51B /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/JumpBackIn.strings; sourceTree = ""; }; @@ -4283,8 +4397,10 @@ 43858A832AF902E40010A4B7 /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = kab.lproj/Shopping.strings; sourceTree = ""; }; 438605AE2C3C010600C4CD23 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/EditAddress.strings; sourceTree = ""; }; 438605AF2C3C010600C4CD23 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/ScanQRCode.strings; sourceTree = ""; }; + 438613FD2C7C9E05005E3A65 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/NativeErrorPage.strings; sourceTree = ""; }; 43866D292B8CAA5C00808188 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/KeyboardAccessory.strings; sourceTree = ""; }; 43866D2A2B8CAA5C00808188 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/PasswordAutofill.strings; sourceTree = ""; }; + 4386E32C2C57AFCF00C97DCD /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Toolbar.strings; sourceTree = ""; }; 438712062BCD42B6003E6870 /* eo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eo; path = eo.lproj/BottomSheet.strings; sourceTree = ""; }; 4387326C28B39ED800FB7454 /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/BookmarkPanel.strings; sourceTree = ""; }; 43879B6F2B399B6700015597 /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = kab.lproj/CredentialProvider.strings; sourceTree = ""; }; @@ -4324,6 +4440,7 @@ 438998AF2B42D59F00591121 /* hy-AM */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "hy-AM"; path = "hy-AM.lproj/Credentials.strings"; sourceTree = ""; }; 438998B02B42D59F00591121 /* hy-AM */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "hy-AM"; path = "hy-AM.lproj/FirefoxLogins.strings"; sourceTree = ""; }; 438998B12B42D59F00591121 /* hy-AM */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "hy-AM"; path = "hy-AM.lproj/LoginsHelper.strings"; sourceTree = ""; }; + 4389B08A2C57B27500C52A68 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Toolbar.strings; sourceTree = ""; }; 4389EEC42AC1A40100C748E8 /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/TabLocation.strings; sourceTree = ""; }; 438A4B202C20535600621E8F /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/ActivityStream.strings; sourceTree = ""; }; 438A4B212C20535600621E8F /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/AddressToolbar.strings; sourceTree = ""; }; @@ -4335,12 +4452,17 @@ 438AEF732AC1A29C00908F58 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/TabLocation.strings; sourceTree = ""; }; 438B138E28B39FB900E53858 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/JumpBackIn.strings; sourceTree = ""; }; 438B138F28B39FB900E53858 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/ToolbarLocation.strings; sourceTree = ""; }; + 438B16422C57B39C007BD1A3 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/EditAddress.strings; sourceTree = ""; }; + 438B16432C57B39C007BD1A3 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/ScanQRCode.strings; sourceTree = ""; }; + 438B16442C57B39C007BD1A3 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Toolbar.strings; sourceTree = ""; }; 438B41BA2B5E865F00BA2E52 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/CredentialProvider.strings; sourceTree = ""; }; 438B41BB2B5E865F00BA2E52 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Credentials.strings; sourceTree = ""; }; 438B41BC2B5E865F00BA2E52 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/FirefoxLogins.strings; sourceTree = ""; }; 438B41BD2B5E865F00BA2E52 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/LoginsHelper.strings; sourceTree = ""; }; 438B41BE2B5E865F00BA2E52 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/TabToolbar.strings; sourceTree = ""; }; + 438C39022C57B042005CD2CC /* es-AR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-AR"; path = "es-AR.lproj/Toolbar.strings"; sourceTree = ""; }; 438C427F29D1B1EB0088717D /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/SnackBar.strings; sourceTree = ""; }; + 438C433E2C7C9D9F005AB958 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/NativeErrorPage.strings"; sourceTree = ""; }; 438C789C2BE8F22600265DFC /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/Microsurvey.strings"; sourceTree = ""; }; 438C8BD62C3C039500F49EC2 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/EditAddress.strings; sourceTree = ""; }; 438C8BD72C3C039500F49EC2 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/ScanQRCode.strings; sourceTree = ""; }; @@ -4349,6 +4471,7 @@ 438CA81D2B9F1D6900B17028 /* co */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = co; path = co.lproj/PasswordAutofill.strings; sourceTree = ""; }; 438CAA3B2A52E67C00857596 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/CustomizeFirefoxHome.strings; sourceTree = ""; }; 438CAA3C2A52E67C00857596 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/SelectCreditCard.strings; sourceTree = ""; }; + 438CC1982C453A43000BC4CF /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/EditAddress.strings; sourceTree = ""; }; 438D48862BCD44730052F1E1 /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = kab.lproj/BottomSheet.strings; sourceTree = ""; }; 438D48872BCD44730052F1E1 /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = kab.lproj/Credentials.strings; sourceTree = ""; }; 438D71902BE8F3010097636A /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/Microsurvey.strings; sourceTree = ""; }; @@ -4357,6 +4480,7 @@ 438DF00B2959B85B005E4C8F /* en-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-CA"; path = "en-CA.lproj/SearchHeaderTitle.strings"; sourceTree = ""; }; 438DF00C2959B85B005E4C8F /* en-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-CA"; path = "en-CA.lproj/TabsTray.strings"; sourceTree = ""; }; 438E69C22BA85A1200DE4241 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/BottomSheet.strings; sourceTree = ""; }; + 438E812A2C57B30F004E1492 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/EditAddress.strings; sourceTree = ""; }; 438EA1322C298BCA00EF793D /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/ActivityStream.strings; sourceTree = ""; }; 438EA1332C298BCA00EF793D /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/AddressToolbar.strings; sourceTree = ""; }; 438EA1342C298BCA00EF793D /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; @@ -4377,9 +4501,14 @@ 43907D302C2050FB00DA0338 /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/EnhancedTrackingProtection.strings"; sourceTree = ""; }; 43907D312C2050FB00DA0338 /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/QRCode.strings"; sourceTree = ""; }; 43907E4C2B42D4550053503C /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Credentials.strings; sourceTree = ""; }; + 4390F7E12C57B1E700F52D3C /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/EditAddress.strings; sourceTree = ""; }; + 4390F7E22C57B1E700F52D3C /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/ScanQRCode.strings; sourceTree = ""; }; + 4390F7E32C57B1E800F52D3C /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Toolbar.strings; sourceTree = ""; }; + 4391326B2C57B0BB00805AE1 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Toolbar.strings; sourceTree = ""; }; 43914B522B4C115A0028781C /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/ContextualHints.strings; sourceTree = ""; }; 43914B532B4C115B0028781C /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/Credentials.strings; sourceTree = ""; }; 43914B542B4C115B0028781C /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/FirefoxHomepage.strings; sourceTree = ""; }; + 4391631F2C60EA8C00263662 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Toolbar.strings; sourceTree = ""; }; 43922AF72BCD4442000D4B12 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/BottomSheet.strings; sourceTree = ""; }; 4392732B2A6E95B50014E253 /* su */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = su; path = su.lproj/Alert.strings; sourceTree = ""; }; 4392732C2A6E95B50014E253 /* su */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = su; path = su.lproj/DisplayCard.strings; sourceTree = ""; }; @@ -4415,6 +4544,8 @@ 43939B4F29E4277D00ADEA2A /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = "kab.lproj/Edit Card.strings"; sourceTree = ""; }; 4393C0C32A24BFE90013D0A2 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/DisplayCard.strings; sourceTree = ""; }; 439404FD29E4273000BCE280 /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = "ia.lproj/Edit Card.strings"; sourceTree = ""; }; + 43940EE22C7C9C0D000AA60B /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/NativeErrorPage.strings; sourceTree = ""; }; + 439410F22C7C9E1400328C59 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/NativeErrorPage.strings; sourceTree = ""; }; 43942A67293E0A64001BD183 /* eo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eo; path = eo.lproj/Alerts.strings; sourceTree = ""; }; 43942A68293E0A64001BD183 /* eo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eo; path = eo.lproj/SearchHeaderTitle.strings; sourceTree = ""; }; 43942A69293E0A64001BD183 /* eo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eo; path = eo.lproj/TabsTray.strings; sourceTree = ""; }; @@ -4424,6 +4555,7 @@ 439532AD28BE8D9B00B79237 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/ToolbarLocation.strings; sourceTree = ""; }; 4395FBDC2B5E85D200AA7AF3 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/TabToolbar.strings; sourceTree = ""; }; 43960B552ADD551700E3DF6B /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/Share.strings; sourceTree = ""; }; + 43961E7E2C57B15B00762948 /* hsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hsb; path = hsb.lproj/Toolbar.strings; sourceTree = ""; }; 43962D222A24C16600934337 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/DisplayCard.strings; sourceTree = ""; }; 4396410329C87A6700F74173 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/Alert.strings"; sourceTree = ""; }; 4396410429C87A6700F74173 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/BiometricAuthentication.strings"; sourceTree = ""; }; @@ -4431,9 +4563,11 @@ 4396410629C87A6700F74173 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/ErrorState.strings"; sourceTree = ""; }; 4396410729C87A6700F74173 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/Settings.strings"; sourceTree = ""; }; 4396410829C87A6700F74173 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/SnackBar.strings"; sourceTree = ""; }; + 43964FF82C7C9E89001C730A /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/NativeErrorPage.strings; sourceTree = ""; }; 439691A02AEFC7C900ED7ADA /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = kk.lproj/Shopping.strings; sourceTree = ""; }; 4396BBE32A0BCCD200482ABA /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/FirefoxSync.strings; sourceTree = ""; }; 4396BBE42A0BCCD200482ABA /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/Upgrade.strings; sourceTree = ""; }; + 4396C29A2C57B42700837488 /* si */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = si; path = si.lproj/EditAddress.strings; sourceTree = ""; }; 4396CC192BCD424B000EB7C7 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/BottomSheet.strings; sourceTree = ""; }; 4396E5F72A373433008E15B7 /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/Footer.strings"; sourceTree = ""; }; 4396E5F82A373433008E15B7 /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/RememberCard.strings"; sourceTree = ""; }; @@ -4468,6 +4602,7 @@ 4398CDB82AC1A2FF00C9AA9E /* ka */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ka; path = ka.lproj/Share.strings; sourceTree = ""; }; 4398CDB92AC1A2FF00C9AA9E /* ka */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ka; path = ka.lproj/TabLocation.strings; sourceTree = ""; }; 4399EF912B7BEFC600E091BE /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = kab.lproj/TabToolbar.strings; sourceTree = ""; }; + 439A03F62C57B051003675A3 /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/Toolbar.strings"; sourceTree = ""; }; 439A0C6E29FFD5DA0084BD94 /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/Notification.strings; sourceTree = ""; }; 439A0C6F29FFD5DB0084BD94 /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/ZoomPageBar.strings; sourceTree = ""; }; 439A220E29F69A0C00F120EE /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/Notification.strings; sourceTree = ""; }; @@ -4487,6 +4622,7 @@ 439A9B0F2C2052310071A727 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/AddressToolbar.strings; sourceTree = ""; }; 439A9B102C2052310071A727 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; 439A9B112C2052310071A727 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/QRCode.strings; sourceTree = ""; }; + 439A9F662C57B3AF00448564 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Toolbar.strings"; sourceTree = ""; }; 439B78172A09721600CAAE37 /* FormAutofillHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormAutofillHelperTests.swift; sourceTree = ""; }; 439BC6462AC1A4E000AF4D58 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Share.strings; sourceTree = ""; }; 439BC6472AC1A4E000AF4D58 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/TabLocation.strings; sourceTree = ""; }; @@ -4561,6 +4697,7 @@ 43A03B3329E427890035374E /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = "kk.lproj/Edit Card.strings"; sourceTree = ""; }; 43A03D882A0BCAE30002CBFF /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/FirefoxSync.strings"; sourceTree = ""; }; 43A03D892A0BCAE30002CBFF /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/Upgrade.strings"; sourceTree = ""; }; + 43A04D712C73605C0052F8EB /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/Toolbar.strings; sourceTree = ""; }; 43A076362BCD41BC00149887 /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/BottomSheet.strings; sourceTree = ""; }; 43A076372BCD41BC00149887 /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/KeyboardAccessory.strings; sourceTree = ""; }; 43A173162A24C0B5009C10B1 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/DisplayCard.strings; sourceTree = ""; }; @@ -4588,6 +4725,7 @@ 43A49E682B30607700FC86A9 /* hsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hsb; path = hsb.lproj/Credentials.strings; sourceTree = ""; }; 43A49E692B30607700FC86A9 /* hsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hsb; path = hsb.lproj/FirefoxLogins.strings; sourceTree = ""; }; 43A49E6A2B30607700FC86A9 /* hsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hsb; path = hsb.lproj/LoginsHelper.strings; sourceTree = ""; }; + 43A4B26E2C57B43700EF1B91 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/Toolbar.strings; sourceTree = ""; }; 43A4FE5528B9069200497E4A /* si */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = si; path = si.lproj/JumpBackIn.strings; sourceTree = ""; }; 43A4FE5628B9069200497E4A /* si */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = si; path = si.lproj/ToolbarLocation.strings; sourceTree = ""; }; 43A5515C28B39F1E0077B8F9 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/JumpBackIn.strings; sourceTree = ""; }; @@ -4617,12 +4755,14 @@ 43A7CA152A2DF99E00BCAA15 /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/RememberCard.strings"; sourceTree = ""; }; 43A7CA162A2DF99E00BCAA15 /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/UpdateCard.strings"; sourceTree = ""; }; 43A878162B838FF90039D6B7 /* br */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = br; path = br.lproj/PasswordAutofill.strings; sourceTree = ""; }; + 43A9086F2C57B3C300698450 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Toolbar.strings"; sourceTree = ""; }; 43A9B1E62A124A3800FB58C6 /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/FirefoxSync.strings; sourceTree = ""; }; 43A9B1E72A124A3800FB58C6 /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/Upgrade.strings; sourceTree = ""; }; 43A9DD9A29BA6B3A003D55A4 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/EngagementNotification.strings; sourceTree = ""; }; 43A9DD9B29BA6B3B003D55A4 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Onboarding.strings; sourceTree = ""; }; 43A9DD9C29BA6B3B003D55A4 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/ResearchSurface.strings; sourceTree = ""; }; 43AAE57528B39C790048A19E /* ga */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ga; path = ga.lproj/BookmarkPanel.strings; sourceTree = ""; }; + 43AB384A2C57B33900F19287 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Toolbar.strings; sourceTree = ""; }; 43AB6F9C25DC53D20016B015 /* GoogleTopSiteManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GoogleTopSiteManager.swift; sourceTree = ""; }; 43AB6F9E25DC53D20016B015 /* LabelButtonHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LabelButtonHeaderView.swift; sourceTree = ""; }; 43AB6FA125DC53D30016B015 /* TopSiteHistoryManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TopSiteHistoryManager.swift; sourceTree = ""; }; @@ -4657,6 +4797,7 @@ 43AD73D12C2051EC00C1442B /* hy-AM */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "hy-AM"; path = "hy-AM.lproj/AddressToolbar.strings"; sourceTree = ""; }; 43AD73D22C2051ED00C1442B /* hy-AM */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "hy-AM"; path = "hy-AM.lproj/EnhancedTrackingProtection.strings"; sourceTree = ""; }; 43AD73D32C2051ED00C1442B /* hy-AM */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "hy-AM"; path = "hy-AM.lproj/QRCode.strings"; sourceTree = ""; }; + 43ADBF222C7C9BAF006B6E34 /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/NativeErrorPage.strings; sourceTree = ""; }; 43ADC80B29F69B27004444BA /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Notification.strings; sourceTree = ""; }; 43ADC80C29F69B28004444BA /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/ZoomPageBar.strings; sourceTree = ""; }; 43ADE8FD2B9F21540025934D /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/KeyboardAccessory.strings; sourceTree = ""; }; @@ -4677,6 +4818,7 @@ 43AFD0D329F69A6E0001109D /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/ZoomPageBar.strings; sourceTree = ""; }; 43B083B729F69DC10093BE71 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Notification.strings; sourceTree = ""; }; 43B083B829F69DC10093BE71 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/ZoomPageBar.strings; sourceTree = ""; }; + 43B0B5692C7C9C2A00B2A5AE /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/NativeErrorPage.strings; sourceTree = ""; }; 43B0CCAC29F69BCF00CBDC1D /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Notification.strings; sourceTree = ""; }; 43B0CCAD29F69BCF00CBDC1D /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/ZoomPageBar.strings; sourceTree = ""; }; 43B0CE2328EAFC58000500A2 /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/JumpBackIn.strings; sourceTree = ""; }; @@ -4709,6 +4851,7 @@ 43B1BCFD2B9F1D8E00126EF0 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/PasswordAutofill.strings; sourceTree = ""; }; 43B1EF182C3C019F00648D5A /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/EditAddress.strings; sourceTree = ""; }; 43B1EF192C3C019F00648D5A /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/ScanQRCode.strings; sourceTree = ""; }; + 43B210482C7C9EA500B136AD /* ug */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ug; path = ug.lproj/NativeErrorPage.strings; sourceTree = ""; }; 43B296302B305F1E00A5AA9B /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/ContextualHints.strings; sourceTree = ""; }; 43B296332B305F1E00A5AA9B /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/CredentialProvider.strings; sourceTree = ""; }; 43B296362B305F1E00A5AA9B /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/Credentials.strings; sourceTree = ""; }; @@ -4719,6 +4862,9 @@ 43B2DC8A29BA6BF200810266 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/EngagementNotification.strings; sourceTree = ""; }; 43B2DC8B29BA6BF200810266 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/Onboarding.strings; sourceTree = ""; }; 43B2DC8C29BA6BF200810266 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/ResearchSurface.strings; sourceTree = ""; }; + 43B3869C2C57B446007728B6 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/ScanQRCode.strings; sourceTree = ""; }; + 43B3869D2C57B446007728B6 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/Toolbar.strings; sourceTree = ""; }; + 43B388562C57B1830004C424 /* hy-AM */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "hy-AM"; path = "hy-AM.lproj/Toolbar.strings"; sourceTree = ""; }; 43B3D33329DAEB53006B3FBC /* en-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-CA"; path = "en-CA.lproj/Alert.strings"; sourceTree = ""; }; 43B3D33429DAEB53006B3FBC /* en-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-CA"; path = "en-CA.lproj/BiometricAuthentication.strings"; sourceTree = ""; }; 43B3D33529DAEB53006B3FBC /* en-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-CA"; path = "en-CA.lproj/EditCard.strings"; sourceTree = ""; }; @@ -4757,6 +4903,7 @@ 43B6E77A2AC1A4F600D81CF5 /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/TabLocation.strings"; sourceTree = ""; }; 43B715C62AEFC69100C18469 /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/Shopping.strings"; sourceTree = ""; }; 43B719662A24BF2900A842F5 /* es-AR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-AR"; path = "es-AR.lproj/DisplayCard.strings"; sourceTree = ""; }; + 43B74E662C453D5000CDBC34 /* pa-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pa-IN"; path = "pa-IN.lproj/ScanQRCode.strings"; sourceTree = ""; }; 43B7BFCA28B905E700AAA92F /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/ToolbarLocation.strings; sourceTree = ""; }; 43B7E64828B39D1E0003A141 /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/JumpBackIn.strings; sourceTree = ""; }; 43B7E64928B39D1E0003A141 /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/ToolbarLocation.strings; sourceTree = ""; }; @@ -4782,6 +4929,7 @@ 43B950CD2A2DFC2F0001A8D1 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Footer.strings; sourceTree = ""; }; 43B950CE2A2DFC2F0001A8D1 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/RememberCard.strings; sourceTree = ""; }; 43B950CF2A2DFC2F0001A8D1 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/UpdateCard.strings; sourceTree = ""; }; + 43B956C12C57B4C800F0E77B /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Toolbar.strings; sourceTree = ""; }; 43B966AE2BE8F11D007448EA /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/Microsurvey.strings; sourceTree = ""; }; 43B9A3EB2A2DFA560078C977 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Footer.strings; sourceTree = ""; }; 43B9A3EC2A2DFA560078C977 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/RememberCard.strings; sourceTree = ""; }; @@ -4862,7 +5010,15 @@ 43BE95752A124BAD00DCFD17 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Upgrade.strings; sourceTree = ""; }; 43BED8DE2BE8F1F100E34361 /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/Microsurvey.strings; sourceTree = ""; }; 43BF382B2B8CA9C400EFDAF3 /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/KeyboardAccessory.strings; sourceTree = ""; }; + 43BF3C5F2C7C9C380017A926 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/NativeErrorPage.strings; sourceTree = ""; }; 43BFD4502C3C04B400B76DE5 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/EditAddress.strings; sourceTree = ""; }; + 43BFE5BA2C57B54100CFB4C5 /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/Toolbar.strings"; sourceTree = ""; }; + 43C09A7A2C7C9EB400BCD71D /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/ActivityStream.strings; sourceTree = ""; }; + 43C09A7B2C7C9EB400BCD71D /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/AddressToolbar.strings; sourceTree = ""; }; + 43C09A7C2C7C9EB400BCD71D /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/EditAddress.strings; sourceTree = ""; }; + 43C09A7D2C7C9EB400BCD71D /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; + 43C09A7E2C7C9EB400BCD71D /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/ScanQRCode.strings; sourceTree = ""; }; + 43C09A7F2C7C9EB400BCD71D /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Toolbar.strings; sourceTree = ""; }; 43C0B1D82BE8F26100E527D7 /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/Microsurvey.strings"; sourceTree = ""; }; 43C0DAB929D1B25700827785 /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = kk.lproj/Alert.strings; sourceTree = ""; }; 43C0DABA29D1B25700827785 /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = kk.lproj/BiometricAuthentication.strings; sourceTree = ""; }; @@ -4878,11 +5034,13 @@ 43C19FC129F69DE0005214A6 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/Notification.strings"; sourceTree = ""; }; 43C19FC229F69DE0005214A6 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/ZoomPageBar.strings"; sourceTree = ""; }; 43C22F422BE8F4D80043046A /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/Microsurvey.strings; sourceTree = ""; }; + 43C2B2312C57B4560010E539 /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sq; path = sq.lproj/Toolbar.strings; sourceTree = ""; }; 43C335E028B39F6500858209 /* ta */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ta; path = ta.lproj/BookmarkPanel.strings; sourceTree = ""; }; 43C39FE42A24C0EB000FD072 /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/DisplayCard.strings"; sourceTree = ""; }; 43C4236D2AEFC88A00518309 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Shopping.strings"; sourceTree = ""; }; 43C426C42AEFC69B000D7D40 /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/Shopping.strings"; sourceTree = ""; }; 43C437A72B554B9C00FBA497 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/TabToolbar.strings; sourceTree = ""; }; + 43C4422D2C7C9C4700ED0870 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/NativeErrorPage.strings; sourceTree = ""; }; 43C4A8562C3C002C00379C52 /* dsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = dsb; path = dsb.lproj/EditAddress.strings; sourceTree = ""; }; 43C4A8572C3C002C00379C52 /* dsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = dsb; path = dsb.lproj/ScanQRCode.strings; sourceTree = ""; }; 43C4BFF329BF3DF1005359FB /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/EngagementNotification.strings"; sourceTree = ""; }; @@ -4931,6 +5089,15 @@ 43C993B42B30609900C813F4 /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/FirefoxLogins.strings; sourceTree = ""; }; 43C993B52B30609900C813F4 /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/LoginsHelper.strings; sourceTree = ""; }; 43C9AE552ACADF5500159B34 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Share.strings; sourceTree = ""; }; + 43C9CD422C6A26D8002F86B4 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/ContextualHints.strings; sourceTree = ""; }; + 43C9CD432C6A26D9002F86B4 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/Credentials.strings; sourceTree = ""; }; + 43C9CD442C6A26D9002F86B4 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/PasswordAutofill.strings; sourceTree = ""; }; + 43C9CD452C6A26D9002F86B4 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/QRCode.strings; sourceTree = ""; }; + 43C9CD462C6A26D9002F86B4 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/ResearchSurface.strings; sourceTree = ""; }; + 43C9CD472C6A26D9002F86B4 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/ScanQRCode.strings; sourceTree = ""; }; + 43C9CD482C6A26D9002F86B4 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/Share.strings; sourceTree = ""; }; + 43C9CD492C6A26D9002F86B4 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/TabToolbar.strings; sourceTree = ""; }; + 43C9CD4A2C6A26D9002F86B4 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/ToolbarLocation.strings; sourceTree = ""; }; 43CA0389293E0AB30056EF4B /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/Alerts.strings; sourceTree = ""; }; 43CA038A293E0AB30056EF4B /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/SearchHeaderTitle.strings; sourceTree = ""; }; 43CA038B293E0AB30056EF4B /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/TabsTray.strings; sourceTree = ""; }; @@ -4975,11 +5142,14 @@ 43CD8C0929F69B6900B3ED1C /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/ZoomPageBar.strings; sourceTree = ""; }; 43CE859E2BCD467D00EFFAA4 /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/BottomSheet.strings; sourceTree = ""; }; 43CE90FA2BE8F36700FFB2D4 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/Microsurvey.strings; sourceTree = ""; }; + 43CE9F3D2C57B5500069B3AF /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/Toolbar.strings"; sourceTree = ""; }; 43CEB4582B680459000F85A9 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/ContextualHints.strings"; sourceTree = ""; }; 43CEB4592B680459000F85A9 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Credentials.strings"; sourceTree = ""; }; 43CEB45A2B680459000F85A9 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/TabToolbar.strings"; sourceTree = ""; }; 43CF280028B39A70008DF2C5 /* anp */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = anp; path = anp.lproj/BookmarkPanel.strings; sourceTree = ""; }; + 43CF316F2C7C9F0900D898EE /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/NativeErrorPage.strings"; sourceTree = ""; }; 43CF76D92B5E84B9001CA9CD /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/TabToolbar.strings; sourceTree = ""; }; + 43CFB14C2C7C9A2B003B700F /* co */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = co; path = co.lproj/NativeErrorPage.strings; sourceTree = ""; }; 43CFD03D2B9F1D1000E7EB9E /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/PasswordAutofill.strings; sourceTree = ""; }; 43D00492296FC48F00CB0F31 /* CreditCardSettingsEmptyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreditCardSettingsEmptyView.swift; sourceTree = ""; }; 43D04B0729E425A4007722F2 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = "de.lproj/Edit Card.strings"; sourceTree = ""; }; @@ -5086,6 +5256,7 @@ 43D6F6F32B554C0100328E70 /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/FirefoxLogins.strings; sourceTree = ""; }; 43D6F6F42B554C0100328E70 /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/LoginsHelper.strings; sourceTree = ""; }; 43D6F6F52B554C0100328E70 /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/TabToolbar.strings; sourceTree = ""; }; + 43D6F80C2C57B4E7004BFBB8 /* ug */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ug; path = ug.lproj/Toolbar.strings; sourceTree = ""; }; 43D72DB226FCE3550069BDE9 /* mr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = mr; path = "mr.lproj/Default Browser.strings"; sourceTree = ""; }; 43D73B492A77CD3300E8C4EB /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/CustomizeFirefoxHome.strings; sourceTree = ""; }; 43D74AE02BCD4286000D0251 /* en-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-CA"; path = "en-CA.lproj/BottomSheet.strings"; sourceTree = ""; }; @@ -5095,6 +5266,9 @@ 43D75D1B2AC1A16900D1DE87 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/TabLocation.strings; sourceTree = ""; }; 43D78C4A2B83937A00C9A5F6 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/KeyboardAccessory.strings; sourceTree = ""; }; 43D78C4B2B83937A00C9A5F6 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/PasswordAutofill.strings; sourceTree = ""; }; + 43D79A832C4539FD0057C720 /* br */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = br; path = br.lproj/ContextualHints.strings; sourceTree = ""; }; + 43D79A842C4539FD0057C720 /* br */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = br; path = br.lproj/EditAddress.strings; sourceTree = ""; }; + 43D79A852C4539FD0057C720 /* br */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = br; path = br.lproj/ScanQRCode.strings; sourceTree = ""; }; 43D79C2C2C3C00AF00DF5925 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/EditAddress.strings; sourceTree = ""; }; 43D79C2D2C3C00AF00DF5925 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/ScanQRCode.strings; sourceTree = ""; }; 43D79ED529225A170047FF5D /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/SearchHeaderTitle.strings"; sourceTree = ""; }; @@ -5178,6 +5352,7 @@ 43DE20222A52E453001EDCCF /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/SelectCreditCard.strings; sourceTree = ""; }; 43DE406A2A0BCB1900D6F533 /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/FirefoxSync.strings"; sourceTree = ""; }; 43DE406B2A0BCB1900D6F533 /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/Upgrade.strings"; sourceTree = ""; }; + 43DE88092C7C9A3B00A7D4E0 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/NativeErrorPage.strings; sourceTree = ""; }; 43DEDA8D2B0B75FF003E54D4 /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/FirefoxHomepage.strings"; sourceTree = ""; }; 43DEE39E296C2CD600D13D0A /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Alerts.strings; sourceTree = ""; }; 43DEE39F296C2CD600D13D0A /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/JumpBackIn.strings; sourceTree = ""; }; @@ -5189,6 +5364,7 @@ 43DF19022C20505300CB9A72 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/AddressToolbar.strings; sourceTree = ""; }; 43DF19032C20505300CB9A72 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; 43DF19042C20505300CB9A72 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/QRCode.strings; sourceTree = ""; }; + 43DF2A222C57AFDD0083E728 /* dsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = dsb; path = dsb.lproj/Toolbar.strings; sourceTree = ""; }; 43DF2D8E29C8799F008CA78E /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/Alert.strings; sourceTree = ""; }; 43DF2D8F29C8799F008CA78E /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/BiometricAuthentication.strings; sourceTree = ""; }; 43DF2D9029C8799F008CA78E /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/EditCard.strings; sourceTree = ""; }; @@ -5213,6 +5389,24 @@ 43DF9459292258C300590FE3 /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/SearchHeaderTitle.strings; sourceTree = ""; }; 43DF990828B39FE2009A5541 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/JumpBackIn.strings; sourceTree = ""; }; 43DF990928B39FE2009A5541 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/ToolbarLocation.strings; sourceTree = ""; }; + 43E0BE322C60EC4B004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/ActivityStream.strings; sourceTree = ""; }; + 43E0BE332C60EC4B004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/AddressToolbar.strings; sourceTree = ""; }; + 43E0BE342C60EC4B004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/BiometricAuthentication.strings; sourceTree = ""; }; + 43E0BE352C60EC4B004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/BottomSheet.strings; sourceTree = ""; }; + 43E0BE362C60EC4B004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/CredentialProvider.strings; sourceTree = ""; }; + 43E0BE372C60EC4B004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/EditAddress.strings; sourceTree = ""; }; + 43E0BE382C60EC4B004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; + 43E0BE392C60EC4B004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/FirefoxHomepage.strings; sourceTree = ""; }; + 43E0BE3A2C60EC4C004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/FirefoxLogins.strings; sourceTree = ""; }; + 43E0BE3B2C60EC4C004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/JumpBackIn.strings; sourceTree = ""; }; + 43E0BE3C2C60EC4C004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/KeyboardAccessory.strings; sourceTree = ""; }; + 43E0BE3D2C60EC4C004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/LoginsHelper.strings; sourceTree = ""; }; + 43E0BE3E2C60EC4C004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/Microsurvey.strings; sourceTree = ""; }; + 43E0BE3F2C60EC4C004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/Notification.strings; sourceTree = ""; }; + 43E0BE402C60EC4C004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/Shopping.strings; sourceTree = ""; }; + 43E0BE412C60EC4C004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/TabLocation.strings; sourceTree = ""; }; + 43E0BE422C60EC4C004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/Toolbar.strings; sourceTree = ""; }; + 43E0BE432C60EC4C004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/ZoomPageBar.strings; sourceTree = ""; }; 43E0E9D728B39CA3002E264A /* gl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gl; path = gl.lproj/BookmarkPanel.strings; sourceTree = ""; }; 43E129A929BA6A480084C419 /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/EngagementNotification.strings; sourceTree = ""; }; 43E129AA29BA6A480084C419 /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/Onboarding.strings; sourceTree = ""; }; @@ -5223,6 +5417,7 @@ 43E18BBF2A24C09100433C0C /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/DisplayCard.strings; sourceTree = ""; }; 43E1D7B029FFD6B5008F2117 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Notification.strings; sourceTree = ""; }; 43E1D7B129FFD6B5008F2117 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/ZoomPageBar.strings; sourceTree = ""; }; + 43E1DCD82C7C9AAB008E952F /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/NativeErrorPage.strings"; sourceTree = ""; }; 43E1FE0129F69D1200F5B25F /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/Alert.strings"; sourceTree = ""; }; 43E1FE0229F69D1200F5B25F /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/BiometricAuthentication.strings"; sourceTree = ""; }; 43E1FE0329F69D1200F5B25F /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/Edit Card.strings"; sourceTree = ""; }; @@ -5319,6 +5514,9 @@ 43E68A5E2B5E83DE004BC945 /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/TabToolbar.strings; sourceTree = ""; }; 43E699ED2AF9014B00030DDD /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Shopping.strings; sourceTree = ""; }; 43E69EAF254D064E00B591C2 /* SimpleTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleTab.swift; sourceTree = ""; }; + 43E6BE132C57AF7600C2152C /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/ScanQRCode.strings; sourceTree = ""; }; + 43E6BE152C57AF7600C2152C /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/Toolbar.strings; sourceTree = ""; }; + 43E7071E2C6A26FC00A29AE4 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Toolbar.strings; sourceTree = ""; }; 43E730C22C20522100892124 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/ActivityStream.strings; sourceTree = ""; }; 43E730C32C20522100892124 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/AddressToolbar.strings; sourceTree = ""; }; 43E730C42C20522100892124 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; @@ -5401,6 +5599,7 @@ 43EBF7072A3735DC00E56F21 /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/RememberCard.strings; sourceTree = ""; }; 43EBF7082A3735DC00E56F21 /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/UpdateCard.strings; sourceTree = ""; }; 43EC35B528B39E0F00E3513B /* mr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = mr; path = mr.lproj/BookmarkPanel.strings; sourceTree = ""; }; + 43EC361F2C7C9A4B00824F5E /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/NativeErrorPage.strings; sourceTree = ""; }; 43EC4E1E2BE8F46200290013 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Microsurvey.strings; sourceTree = ""; }; 43EC6B9F2B14B34C00E417F1 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/FirefoxHomepage.strings; sourceTree = ""; }; 43EC79AA2C3C00BF00519D98 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/KeyboardAccessory.strings"; sourceTree = ""; }; @@ -5409,6 +5608,7 @@ 43EC79AD2C3C00BF00519D98 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/Share.strings"; sourceTree = ""; }; 43EC79AE2C3C00BF00519D98 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/Shopping.strings"; sourceTree = ""; }; 43ECB7C52BE8F3410024824F /* hy-AM */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "hy-AM"; path = "hy-AM.lproj/Microsurvey.strings"; sourceTree = ""; }; + 43ED215A2C57AFEC0014F10F /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/Toolbar.strings; sourceTree = ""; }; 43ED29612B305FD800481A75 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/ContextualHints.strings; sourceTree = ""; }; 43ED29622B305FD800481A75 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/CredentialProvider.strings; sourceTree = ""; }; 43ED29632B305FD800481A75 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Credentials.strings; sourceTree = ""; }; @@ -5426,6 +5626,7 @@ 43EE2FFB29FFD7D700C97C08 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Notification.strings; sourceTree = ""; }; 43EE2FFC29FFD7D700C97C08 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/ZoomPageBar.strings; sourceTree = ""; }; 43EE7D6828B39E20007D5A02 /* ms */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ms; path = ms.lproj/BookmarkPanel.strings; sourceTree = ""; }; + 43EE827C2C7C9D4600CDF5B9 /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/NativeErrorPage.strings; sourceTree = ""; }; 43EE84312BA85BB9003334F5 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/BottomSheet.strings"; sourceTree = ""; }; 43EECA382C3C00560032D70E /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/EditAddress.strings"; sourceTree = ""; }; 43EECA392C3C00560032D70E /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/ScanQRCode.strings"; sourceTree = ""; }; @@ -5435,6 +5636,7 @@ 43EF6F99293E0CB8006A4C7D /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/TabsTray.strings; sourceTree = ""; }; 43EF88BD2A3733B300AB9AAB /* co */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = co; path = co.lproj/Footer.strings; sourceTree = ""; }; 43EF88BE2A3733B300AB9AAB /* co */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = co; path = co.lproj/RememberCard.strings; sourceTree = ""; }; + 43F013FF2C7C9ABA00F8CF52 /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/NativeErrorPage.strings"; sourceTree = ""; }; 43F034172AC1A2E800021EA0 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/TabLocation.strings; sourceTree = ""; }; 43F084E92A52E49F00C35028 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/CustomizeFirefoxHome.strings; sourceTree = ""; }; 43F084EA2A52E49F00C35028 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/SelectCreditCard.strings; sourceTree = ""; }; @@ -5488,6 +5690,8 @@ 43F30EF62B3060BB00336EBE /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/FirefoxLogins.strings; sourceTree = ""; }; 43F30EF72B3060BB00336EBE /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/LoginsHelper.strings; sourceTree = ""; }; 43F349362B5549CC00737A91 /* es-AR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-AR"; path = "es-AR.lproj/TabToolbar.strings"; sourceTree = ""; }; + 43F362712C453A1900F14754 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/EditAddress.strings; sourceTree = ""; }; + 43F362722C453A1900F14754 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/ScanQRCode.strings; sourceTree = ""; }; 43F37875293E0B7C005F1168 /* ka */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ka; path = ka.lproj/Alerts.strings; sourceTree = ""; }; 43F37876293E0B7C005F1168 /* ka */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ka; path = ka.lproj/TabsTray.strings; sourceTree = ""; }; 43F3AB252B554C7600CAEC9A /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/TabToolbar.strings; sourceTree = ""; }; @@ -5516,6 +5720,8 @@ 43F678FB2A52E4660089C9D9 /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/SelectCreditCard.strings"; sourceTree = ""; }; 43F69AD22AEFC6C300F87C1B /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/Shopping.strings"; sourceTree = ""; }; 43F6CC4929EF074A00121D35 /* hy-AM */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "hy-AM"; path = "hy-AM.lproj/Edit Card.strings"; sourceTree = ""; }; + 43F6E7622C453DF70018EEA4 /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sq; path = sq.lproj/EditAddress.strings; sourceTree = ""; }; + 43F6E7632C453DF70018EEA4 /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sq; path = sq.lproj/ScanQRCode.strings; sourceTree = ""; }; 43F78D602A24BF6600E8A9EC /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/DisplayCard.strings; sourceTree = ""; }; 43F7952425795F69005AEE40 /* SearchTelemetry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTelemetry.swift; sourceTree = ""; }; 43F7DAE62C20539100A29EA3 /* pa-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pa-IN"; path = "pa-IN.lproj/ActivityStream.strings"; sourceTree = ""; }; @@ -5575,6 +5781,7 @@ 43FAF1A82C2050C200C52557 /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/QRCode.strings"; sourceTree = ""; }; 43FB409E2A0BCB650006C10B /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/FirefoxSync.strings; sourceTree = ""; }; 43FB409F2A0BCB650006C10B /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Upgrade.strings; sourceTree = ""; }; + 43FB540B2C57AFFA00562B25 /* en-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-CA"; path = "en-CA.lproj/Toolbar.strings"; sourceTree = ""; }; 43FBF67F2ACADCF400F86E9E /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/Share.strings; sourceTree = ""; }; 43FC1AAB2AC1A22C00989CA0 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Share.strings; sourceTree = ""; }; 43FC1AAC2AC1A22C00989CA0 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/TabLocation.strings; sourceTree = ""; }; @@ -5591,6 +5798,10 @@ 43FEB816293E0C3A00DBC665 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Alerts.strings; sourceTree = ""; }; 43FEB817293E0C3A00DBC665 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/SearchHeaderTitle.strings; sourceTree = ""; }; 43FEB818293E0C3A00DBC665 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/TabsTray.strings; sourceTree = ""; }; + 43FECBDA2C7C9AC900D644AE /* eo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eo; path = eo.lproj/EditAddress.strings; sourceTree = ""; }; + 43FECBDB2C7C9AC900D644AE /* eo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eo; path = eo.lproj/NativeErrorPage.strings; sourceTree = ""; }; + 43FECBDC2C7C9ACA00D644AE /* eo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eo; path = eo.lproj/ScanQRCode.strings; sourceTree = ""; }; + 43FECBDD2C7C9ACA00D644AE /* eo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eo; path = eo.lproj/Toolbar.strings; sourceTree = ""; }; 43FEE9662A24C0A3001C35F7 /* or */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = or; path = "or.lproj/Default Browser.strings"; sourceTree = ""; }; 43FF69D12C2050EC00D4B9C3 /* es-AR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-AR"; path = "es-AR.lproj/ActivityStream.strings"; sourceTree = ""; }; 43FF69D22C2050EC00D4B9C3 /* es-AR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-AR"; path = "es-AR.lproj/AddressToolbar.strings"; sourceTree = ""; }; @@ -6050,6 +6261,8 @@ 797548C8B7F6A801A090D219 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/ClearHistoryConfirm.strings; sourceTree = ""; }; 79B14B7CBAE86AEB3730EB33 /* mr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = mr; path = mr.lproj/PrivateBrowsing.strings; sourceTree = ""; }; 7A0C4AEAA9CB35000E35A5BB /* en-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-CA"; path = "en-CA.lproj/PrivateBrowsing.strings"; sourceTree = ""; }; + 7A352B762C4F196B00359D51 /* LegacyTabPeekPreviewActionBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyTabPeekPreviewActionBuilderTests.swift; sourceTree = ""; }; + 7A6DF9EA2BFECC3C00A0C608 /* LegacyTabPeekPreviewActionBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyTabPeekPreviewActionBuilder.swift; sourceTree = ""; }; 7A704C668511CA18CA660BAA /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/InfoPlist.strings"; sourceTree = ""; }; 7A7741BB976626FBB10E5BDC /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/ClearPrivateData.strings; sourceTree = ""; }; 7A7F476EBF03E15BDE5A3C43 /* en-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-CA"; path = "en-CA.lproj/ClearPrivateData.strings"; sourceTree = ""; }; @@ -6058,6 +6271,7 @@ 7AC2423EA6277664A0D3398B /* af */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = af; path = af.lproj/InfoPlist.strings; sourceTree = ""; }; 7ACC4EB59896863B677D102A /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/ClearHistoryConfirm.strings; sourceTree = ""; }; 7AD14377A3F3B824C049E709 /* ta */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ta; path = ta.lproj/PrivateBrowsing.strings; sourceTree = ""; }; + 7ADC1D182C27D35B003ED924 /* ActionProviderBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionProviderBuilder.swift; sourceTree = ""; }; 7AEF4108BAAE5BBD5949E64C /* en-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-CA"; path = "en-CA.lproj/FindInPage.strings"; sourceTree = ""; }; 7B0A456E95C86CC158227349 /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = kab.lproj/PrivateBrowsing.strings; sourceTree = ""; }; 7B0B49C7B69DC54BC3CA5972 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/ErrorPages.strings; sourceTree = ""; }; @@ -6317,6 +6531,7 @@ 8A5604F729DF0D2600035CA3 /* BrowserCoordinatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowserCoordinatorTests.swift; sourceTree = ""; }; 8A57519827AD80B800A84DBF /* ReaderModeStyleViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderModeStyleViewModel.swift; sourceTree = ""; }; 8A590C6028C123100032F1AA /* OpenPassBookHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenPassBookHelper.swift; sourceTree = ""; }; + 8A5BC1EA2C4AA13F009A40A4 /* FeatureFlagsBoolSetting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureFlagsBoolSetting.swift; sourceTree = ""; }; 8A5BD9582878871B000FE773 /* TopSitesHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopSitesHelperTests.swift; sourceTree = ""; }; 8A5BD95B2878AA74000FE773 /* PinnedSite.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinnedSite.swift; sourceTree = ""; }; 8A5BD95E2878B7B6000FE773 /* TopSitesWidgetManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopSitesWidgetManager.swift; sourceTree = ""; }; @@ -6391,6 +6606,7 @@ 8A8629E52880B69C0096DDB1 /* BookmarksPanelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksPanelTests.swift; sourceTree = ""; }; 8A86DAD7277298DE00D7BFFF /* ClosedTabsStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClosedTabsStoreTests.swift; sourceTree = ""; }; 8A87AEE02C1B4D17007428B2 /* SearchViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchViewModelTests.swift; sourceTree = ""; }; + 8A880C432C63CFE200B77F23 /* MockLoginViewModelDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockLoginViewModelDelegate.swift; sourceTree = ""; }; 8A8917682B57283B008B01EA /* HomepageHeaderCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomepageHeaderCell.swift; sourceTree = ""; }; 8A8BAE152B2119E600D774EB /* InternalURL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InternalURL.swift; sourceTree = ""; }; 8A8DDEBE276259A900E7B97A /* RatingPromptManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RatingPromptManager.swift; sourceTree = ""; }; @@ -6410,6 +6626,8 @@ 8A95FF652B1E977E00AC303D /* TelemetryContextualIdentifierTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelemetryContextualIdentifierTests.swift; sourceTree = ""; }; 8A96C4B828F9DD8700B75884 /* ThemableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemableTests.swift; sourceTree = ""; }; 8A96C4BA28F9E7B300B75884 /* XCTestCaseRootViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCTestCaseRootViewController.swift; sourceTree = ""; }; + 8A97E6E92C58487900F94793 /* AddressLocaleFeatureValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressLocaleFeatureValidator.swift; sourceTree = ""; }; + 8A97E6ED2C584AC300F94793 /* AddressLocaleFeatureValidatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressLocaleFeatureValidatorTests.swift; sourceTree = ""; }; 8A99DB9727C6DD3E007EA6BD /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = "is.lproj/Default Browser.strings"; sourceTree = ""; }; 8A99DB9827C6DD3E007EA6BD /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/Today.strings; sourceTree = ""; }; 8A99DB9927C6DD3E007EA6BD /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/BookmarkPanel.strings; sourceTree = ""; }; @@ -6428,6 +6646,7 @@ 8AAAB0582C1B723F008830B3 /* MockRustFirefoxSuggest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockRustFirefoxSuggest.swift; sourceTree = ""; }; 8AAAB05A2C1B7268008830B3 /* ClientTabsSearchWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientTabsSearchWrapper.swift; sourceTree = ""; }; 8AAAB05E2C1B72C9008830B3 /* SearchHighlightItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchHighlightItem.swift; sourceTree = ""; }; + 8AABB92C2C64F77000F1FE51 /* LoginProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginProvider.swift; sourceTree = ""; }; 8AABBCFB2A0010900089941E /* GleanWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GleanWrapper.swift; sourceTree = ""; }; 8AABBCFD2A0017560089941E /* MockGleanWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockGleanWrapper.swift; sourceTree = ""; }; 8AABBD002A001ADF0089941E /* ApplicationHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationHelper.swift; sourceTree = ""; }; @@ -6453,6 +6672,9 @@ 8AB8574527D97CB00075C173 /* HomepageContextMenuProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomepageContextMenuProtocol.swift; sourceTree = ""; }; 8AB8574727D97CD40075C173 /* HomePanelType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomePanelType.swift; sourceTree = ""; }; 8AB8574927D97CE90075C173 /* HomePanelDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomePanelDelegate.swift; sourceTree = ""; }; + 8AB893A22C73AF5200DAEED7 /* MockCreditCardProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockCreditCardProvider.swift; sourceTree = ""; }; + 8AB893A52C73AFBA00DAEED7 /* MockLoginProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockLoginProvider.swift; sourceTree = ""; }; + 8AB893A82C73CBBD00DAEED7 /* CreditCardProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreditCardProvider.swift; sourceTree = ""; }; 8ABA9C8A28931207002C0077 /* JumpBackInDataAdaptorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JumpBackInDataAdaptorTests.swift; sourceTree = ""; }; 8ABA9C8C28931223002C0077 /* MockDispatchQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockDispatchQueue.swift; sourceTree = ""; }; 8ABC5AED284532C900FEA552 /* PocketDiscoverCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PocketDiscoverCell.swift; sourceTree = ""; }; @@ -6512,6 +6734,8 @@ 8AE80BB92891C0C300BC12EA /* JumpBackInSectionLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JumpBackInSectionLayout.swift; sourceTree = ""; }; 8AE80BBB2891C20D00BC12EA /* JumpBackInList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JumpBackInList.swift; sourceTree = ""; }; 8AE80BBD2891C21A00BC12EA /* JumpBackInSyncedTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JumpBackInSyncedTab.swift; sourceTree = ""; }; + 8AEAD9F22C3D7B3E001A2C5A /* FeatureFlagsSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureFlagsSettings.swift; sourceTree = ""; }; + 8AEAD9F42C3D7BA9001A2C5A /* FeatureFlagsDebugViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureFlagsDebugViewController.swift; sourceTree = ""; }; 8AEAD9F62C3DB0BF001A2C5A /* MicrosurveyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MicrosurveyTests.swift; sourceTree = ""; }; 8AED23C427AC1F9500DE7E97 /* BaseContentStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseContentStackView.swift; sourceTree = ""; }; 8AED868228CA3B3400351A50 /* BookmarkPanelViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkPanelViewModelTests.swift; sourceTree = ""; }; @@ -6894,6 +7118,12 @@ AB6FEA1F2AEA5CA200E7B2F2 /* FakespotAdView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakespotAdView.swift; sourceTree = ""; }; AB7C4D658AB44D577390B61F /* an */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = an; path = an.lproj/Menu.strings; sourceTree = ""; }; AB7D4C3029ACAED100626427 /* Tab+ChangeUserAgentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Tab+ChangeUserAgentTests.swift"; sourceTree = ""; }; + AB936A682C05F2B100600F82 /* TrackingProtectionButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackingProtectionButton.swift; sourceTree = ""; }; + AB9CBBFE2C53B64B00102610 /* TrackingProtectionAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrackingProtectionAction.swift; sourceTree = ""; }; + AB9CBBFF2C53B64B00102610 /* TrackingProtectionMiddleware.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrackingProtectionMiddleware.swift; sourceTree = ""; }; + AB9CBC002C53B64B00102610 /* TrackingProtectionModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrackingProtectionModel.swift; sourceTree = ""; }; + AB9CBC012C53B64B00102610 /* TrackingProtectionState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrackingProtectionState.swift; sourceTree = ""; }; + AB9CBC062C53B76400102610 /* TrackingProtectionStateTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrackingProtectionStateTests.swift; sourceTree = ""; }; ABB507CD2A136FB2009CAA67 /* UserConversionMetricsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserConversionMetricsTests.swift; sourceTree = ""; }; ABE4393D2AC432040074FFE1 /* PartnerWebsites.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PartnerWebsites.swift; sourceTree = ""; }; ABEF80D02A24D2BE003F52C4 /* CreditCardBottomSheetViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreditCardBottomSheetViewModel.swift; sourceTree = ""; }; @@ -6943,6 +7173,7 @@ B18D4F2291BA5459431EDCA9 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/Shared.strings"; sourceTree = ""; }; B19648D5B3667FE0CB25D069 /* my */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = my; path = my.lproj/Menu.strings; sourceTree = ""; }; B1C044CA95EB3F5258D2EC51 /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/InfoPlist.strings; sourceTree = ""; }; + B1CA62812C0DB43600D31625 /* MultiWindowTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiWindowTests.swift; sourceTree = ""; }; B1D14C77AD55FE126B7904D8 /* oc */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = oc; path = oc.lproj/PrivateBrowsing.strings; sourceTree = ""; }; B1D74C6B9DE786A50986BB1A /* ur */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ur; path = "ur.lproj/Default Browser.strings"; sourceTree = ""; }; B1F04E16A0BF365D67AD0C6F /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/Search.strings; sourceTree = ""; }; @@ -7991,6 +8222,8 @@ E15DE7C3293A7B0F00B32667 /* PhotonActionSheetTitleHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotonActionSheetTitleHeaderView.swift; sourceTree = ""; }; E16258EE2A83BE0800522742 /* FakespotLoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakespotLoadingView.swift; sourceTree = ""; }; E1634E1AB9F05311CD6C8C6C /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/AuthenticationManager.strings; sourceTree = ""; }; + E16941B32C4E4A2E00FF5F4E /* BrowserViewControllerStateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowserViewControllerStateTests.swift; sourceTree = ""; }; + E16941B72C5119A200FF5F4E /* Autocompletable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Autocompletable.swift; sourceTree = ""; }; E169C6E72979CA0E0017B8D7 /* URLMailTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLMailTests.swift; sourceTree = ""; }; E16AD22B2A8A7AE800F0AA58 /* FakespotHighlightsCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakespotHighlightsCardView.swift; sourceTree = ""; }; E16C76802ABDC0DB00172DB5 /* FakespotHighlightsCardViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakespotHighlightsCardViewModelTests.swift; sourceTree = ""; }; @@ -8001,7 +8234,6 @@ E17496392992B42C0096900A /* CreditCardSectionHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreditCardSectionHeader.swift; sourceTree = ""; }; E174963B2992B6A60096900A /* HostingTableViewSectionHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostingTableViewSectionHeader.swift; sourceTree = ""; }; E174963F2994302D0096900A /* PreferredFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferredFont.swift; sourceTree = ""; }; - E17798952BD6B33300F6F0EB /* ToolbarFlagManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolbarFlagManager.swift; sourceTree = ""; }; E17798972BD6B44B00F6F0EB /* AddressToolbarContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressToolbarContainer.swift; sourceTree = ""; }; E17798992BD7D44200F6F0EB /* ToolbarState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolbarState.swift; sourceTree = ""; }; E177989B2BD7D48500F6F0EB /* ToolbarAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolbarAction.swift; sourceTree = ""; }; @@ -8730,6 +8962,7 @@ C81AC6B526160091007800C5 /* LegacyTabTrayViewModel.swift */, 968BD7EA27DFF0F8003148B3 /* ASGroup.swift */, 216C133D29DCA8FF0097533B /* LegacyTabLayoutDelegate.swift */, + 7A6DF9EA2BFECC3C00A0C608 /* LegacyTabPeekPreviewActionBuilder.swift */, ); path = Legacy; sourceTree = ""; @@ -9399,6 +9632,7 @@ 3BF4B8DA1D38493300493393 /* Utils */, EB7A651020699BD200B52A5F /* WebPagesForTesting.swift */, 0B9D40781E8D5AC80059E664 /* XCUITests-Bridging-Header.h */, + B1CA62812C0DB43600D31625 /* MultiWindowTests.swift */, ); path = XCUITests; sourceTree = ""; @@ -9569,6 +9803,7 @@ isa = PBXGroup; children = ( 5A2918CA2B522338002B197E /* GeneralBrowserAction.swift */, + 7ADC1D182C27D35B003ED924 /* ActionProviderBuilder.swift */, ); path = Actions; sourceTree = ""; @@ -9687,6 +9922,22 @@ path = Generated; sourceTree = ""; }; + 7A352B722C4F190F00359D51 /* Tabs */ = { + isa = PBXGroup; + children = ( + 7A352B732C4F191500359D51 /* Legacy */, + ); + path = Tabs; + sourceTree = ""; + }; + 7A352B732C4F191500359D51 /* Legacy */ = { + isa = PBXGroup; + children = ( + 7A352B762C4F196B00359D51 /* LegacyTabPeekPreviewActionBuilderTests.swift */, + ); + path = Legacy; + sourceTree = ""; + }; 7B0B1B9C1C1B69F500DF4AB5 /* Extensions */ = { isa = PBXGroup; children = ( @@ -10003,6 +10254,7 @@ 8A3EF7EE2A2FCF0000796E3A /* Debug */ = { isa = PBXGroup; children = ( + 8ABCBE622C485CAA00480A21 /* FeatureFlags */, 8CFD56872AAF057D003157A6 /* SwitchFakespotProduction.swift */, 8A3EF7FC2A2FCFAC00796E3A /* AppReviewPromptSetting.swift */, 1DA710062AE7106B00677F6B /* AppDataUsageReportSetting.swift */, @@ -10256,6 +10508,15 @@ path = Extensions; sourceTree = ""; }; + 8A97E6EC2C58489C00F94793 /* AddressUtility */ = { + isa = PBXGroup; + children = ( + 8CEDF07F2BFE138B00D2617B /* AddressProvider.swift */, + 8A97E6E92C58487900F94793 /* AddressLocaleFeatureValidator.swift */, + ); + path = AddressUtility; + sourceTree = ""; + }; 8A9B87A92C1B30340042B894 /* Search */ = { isa = PBXGroup; children = ( @@ -10339,6 +10600,25 @@ path = LogoHeader; sourceTree = ""; }; + 8AB893A12C73AF4500DAEED7 /* Mocks */ = { + isa = PBXGroup; + children = ( + 8AB893A22C73AF5200DAEED7 /* MockCreditCardProvider.swift */, + 8AB893A52C73AFBA00DAEED7 /* MockLoginProvider.swift */, + ); + path = Mocks; + sourceTree = ""; + }; + 8ABCBE622C485CAA00480A21 /* FeatureFlags */ = { + isa = PBXGroup; + children = ( + 8A5BC1EA2C4AA13F009A40A4 /* FeatureFlagsBoolSetting.swift */, + 8AEAD9F22C3D7B3E001A2C5A /* FeatureFlagsSettings.swift */, + 8AEAD9F42C3D7BA9001A2C5A /* FeatureFlagsDebugViewController.swift */, + ); + path = FeatureFlags; + sourceTree = ""; + }; 8AC225632B6D3F9600CDA7FD /* Telemetry */ = { isa = PBXGroup; children = ( @@ -10761,11 +11041,26 @@ AB2AC6642BD15E2C00022AAB /* TrackingProtection */ = { isa = PBXGroup; children = ( + AB9CBBFE2C53B64B00102610 /* TrackingProtectionAction.swift */, + AB9CBBFF2C53B64B00102610 /* TrackingProtectionMiddleware.swift */, + AB9CBC002C53B64B00102610 /* TrackingProtectionModel.swift */, + AB9CBC012C53B64B00102610 /* TrackingProtectionState.swift */, AB2AC6652BD15E6300022AAB /* CertificatesHandler.swift */, + AB936A682C05F2B100600F82 /* TrackingProtectionButton.swift */, + 0A4978492C53E63200B1E82A /* TrackingProtectionViewController.swift */, ); path = TrackingProtection; sourceTree = ""; }; + AB9CBC0A2C53B9A400102610 /* TrackingProtectionTests */ = { + isa = PBXGroup; + children = ( + 0AFF7F652C7784F000265214 /* TrackingProtectionModelTests.swift */, + AB9CBC062C53B76400102610 /* TrackingProtectionStateTests.swift */, + ); + path = TrackingProtectionTests; + sourceTree = ""; + }; ABEF80CD2A24BEF1003F52C4 /* CreditCardBottomSheet */ = { isa = PBXGroup; children = ( @@ -10773,6 +11068,7 @@ AB42CC722A1F523F003C9594 /* CreditCardBottomSheetViewController.swift */, AB42CC732A1F5240003C9594 /* CreditCardBottomSheetHeaderView.swift */, ABEF80D42A254185003F52C4 /* CreditCardBottomSheetFooterView.swift */, + 8AB893A82C73CBBD00DAEED7 /* CreditCardProvider.swift */, ); path = CreditCardBottomSheet; sourceTree = ""; @@ -10780,9 +11076,11 @@ B23620492B7EAF2C000B1DE7 /* Autofill */ = { isa = PBXGroup; children = ( + 8AB893A12C73AF4500DAEED7 /* Mocks */, 439B78172A09721600CAAE37 /* FormAutofillHelperTests.swift */, 8CCD74722B90A945008F919B /* LoginListViewModelTests.swift */, 8CEDF07D2BFE04B100D2617B /* AddressListViewModelTests.swift */, + 8A97E6ED2C584AC300F94793 /* AddressLocaleFeatureValidatorTests.swift */, ); path = Autofill; sourceTree = ""; @@ -10804,11 +11102,11 @@ B2FEA6892B460CEC0058E616 /* Address */ = { isa = PBXGroup; children = ( + 8A97E6EC2C58489C00F94793 /* AddressUtility */, 8C2937732BF79F0C00146613 /* Edit */, B2FEA68C2B460D390058E616 /* AddressAutofillSettingsViewController.swift */, B2FEA68E2B460D9E0058E616 /* AddressAutofillSettingsViewModel.swift */, B2DFB7DE2B619DB80004CEA5 /* AddressListViewModel.swift */, - 8CEDF07F2BFE138B00D2617B /* AddressProvider.swift */, B2FEA68A2B460D1D0058E616 /* AddressAutofillSettingsView.swift */, B2FEA6902B4661BE0058E616 /* AddressAutofillToggle.swift */, B2DFB7E02B619DF60004CEA5 /* AddressListView.swift */, @@ -11152,6 +11450,7 @@ 434CD57729F6FC4500A0D04B /* MockAppAuthenticator.swift */, 8AABBD022A001CBC0089941E /* MockApplicationHelper.swift */, 965C3C9729343445006499ED /* MockAppSessionManager.swift */, + 0AFF7F632C7784D600265214 /* MockDataCleaner.swift */, 8A5D1C9F2A30C9D7005AD35C /* MockAppSettingsDelegate.swift */, 8A5038132A5DFCE000A1B02A /* MockBrowserProfile.swift */, C8C3FEA029F973C40038E3BA /* MockBrowserViewController.swift */, @@ -11166,6 +11465,7 @@ 8AF10D9029D776190086351D /* MockLaunchScreenManager.swift */, 965C3C95293431FC006499ED /* MockLaunchSessionProvider.swift */, 8AF99B5329EF2AF100108DEC /* MockLogger.swift */, + 8A880C432C63CFE200B77F23 /* MockLoginViewModelDelegate.swift */, 8A93F86629D373AC004159D9 /* MockNavigationController.swift */, 5AB4237B28A1947A003BC40C /* MockNotificationCenter.swift */, E18259E229B2A51B00E6BE76 /* MockNotificationManager.swift */, @@ -11834,6 +12134,14 @@ path = Views; sourceTree = ""; }; + E16941B22C4E498600FF5F4E /* Toolbar */ = { + isa = PBXGroup; + children = ( + E14C78952C105488002AD3C7 /* AddressToolbarContainerModelTests.swift */, + ); + path = Toolbar; + sourceTree = ""; + }; E17496362991A2480096900A /* SwiftUI */ = { isa = PBXGroup; children = ( @@ -11849,8 +12157,8 @@ E1DE2F812BE394C80054BCDC /* Redux */, E17798972BD6B44B00F6F0EB /* AddressToolbarContainer.swift */, E18CE8D92BDA3F6B00EE2BCD /* NavigationToolbarContainer.swift */, - E17798952BD6B33300F6F0EB /* ToolbarFlagManager.swift */, E14792A22C2C5C660058211C /* ToolbarHelper.swift */, + E16941B72C5119A200FF5F4E /* Autocompletable.swift */, ); path = Toolbars; sourceTree = ""; @@ -11909,6 +12217,7 @@ E1AEC173286E0CF500062E29 /* Browser */ = { isa = PBXGroup; children = ( + 7A352B722C4F190F00359D51 /* Tabs */, 21B337BA29B67E4100E4F806 /* BrowserViewControllerWebViewDelegateTests.swift */, F98CB66B2A4123B5005F38E9 /* EnhancedTrackingProtection */, 8A1E3BE828CBC57E003388C4 /* SearchEngines */, @@ -12087,6 +12396,8 @@ 43C50B4E2A0BCAA800C6A134 /* FirefoxSync.strings */, 43A7153B2A2DF94F00DD5747 /* Footer.strings */, E4E0BB171AFBC9E4008D6260 /* Info.plist */, + 437AE30E2C7C9A0C0053F7F3 /* NativeErrorPage.strings */, + 43E6BE142C57AF7600C2152C /* Toolbar.strings */, 43B627742C3C001E00A188E8 /* ScanQRCode.strings */, 43D3ED962C3BFFC900E33D46 /* EditAddress.strings */, 43903A902C20502A00ACF76E /* QRCode.strings */, @@ -12144,6 +12455,7 @@ E1E5BE242A28F7BE00248F77 /* PasswordDetailViewControllerModel.swift */, CAA3B7E52497DCB60094E3C1 /* LoginDataSource.swift */, CA520E7924913C1B00CCAB48 /* PasswordManagerViewModel.swift */, + 8AABB92C2C64F77000F1FE51 /* LoginProvider.swift */, CA7FC7D224A6A9B70012F347 /* PasswordManagerDataSourceHelper.swift */, CA90753724929B22005B794D /* NoLoginsView.swift */, CAC458F0249429C20042561A /* PasswordManagerSelectionHelper.swift */, @@ -12462,11 +12774,10 @@ F84B21D61A090F8100AAB793 /* ClientTests */ = { isa = PBXGroup; children = ( - 8A8482ED2BE15FEF00F9007B /* Microsurvey */, - 8AC225632B6D3F9600CDA7FD /* Telemetry */, 8A171A6029F82AD90085770E /* Application */, - E14C78952C105488002AD3C7 /* AddressToolbarContainerModelTests.swift */, + B23620492B7EAF2C000B1DE7 /* Autofill */, CA7BD564248185B500A0A61B /* BreachAlertsTests.swift */, + E16941B32C4E4A2E00FF5F4E /* BrowserViewControllerStateTests.swift */, BC003F5D2B59F44500929ECB /* BrowserViewControllerTests.swift */, 8A28C627291028870078A81A /* CanRemoveQuickActionBookmarkTests.swift */, F84B21D91A090F8100AAB793 /* ClientTests.swift */, @@ -12475,21 +12786,24 @@ C889D7CD2858C4B500121E1D /* ContextMenuHelperTests.swift */, 8A93F86329D37314004159D9 /* Coordinators */, 43B658D729CE249D00C9EF08 /* CreditCard */, - B23620492B7EAF2C000B1DE7 /* Autofill */, 3B39EDB91E16E18900EF029F /* CustomSearchEnginesTest.swift */, 431C0D1C25C9D76B00395CE4 /* DefaultBrowserOnboardingTests */, D82ED2631FEB3C420059570B /* DefaultSearchPrefsTests.swift */, + 814A62452B587A3E00608195 /* DefaultThemeManagerTests.swift */, 5A70EF17295E2DF400790249 /* DependencyInjection */, 8A11C8122731E54800AC7318 /* DictionaryExtensionsTests.swift */, 6ADB651A285C03B100947EA4 /* DownloadHelperTests.swift */, 6A3E5D89283831D0001E706E /* DownloadQueueTests.swift */, + E1E425312B5A2E9700899550 /* DownloadTests.swift */, 1D7B789E2AE088930011E9F2 /* EventQueueTests.swift */, 8A96C4B728F9DD0600B75884 /* Extensions */, DF1E6AA92A964358000D4854 /* Fakespot */, C807CCCB28367446008E6A5A /* FeatureFlagManagerTests.swift */, D3D488581ABB54CD00A93597 /* FileAccessorTests.swift */, + C787D8C22C1CB77900940123 /* FirefoxAccountSignInViewControllerTests.swift */, E1AEC16D286E0CF500062E29 /* Frontend */, 3943A81C1E9807C700D4F6DC /* FxAPushMessageTest.swift */, + 0AC659262BF35854005C614A /* FxAWebViewModelTests.swift */, E1E6F8CD29D4B7E700068D8D /* GleanPlumbContextProviderTests.swift */, 8A6E139D2A71C78A00A88FA8 /* GridTabViewControllerTests.swift */, 8ACA8F722919870B00D3075D /* Helpers */, @@ -12501,6 +12815,7 @@ C8445AD026443C7F00B83F53 /* LibraryPanelViewStateTests.swift */, C8DC90CA2A067BF10008832B /* MarkupParseUtilityTests */, C83DE54529DF3579006E1B69 /* Messaging */, + 8A8482ED2BE15FEF00F9007B /* Microsurvey */, C889D7D22858C85200121E1D /* Mocks */, C8B41E0B29F034D500FE218A /* Nimbus */, E1312FCF29D23775008DDA85 /* NotificationSurface */, @@ -12515,6 +12830,7 @@ 8A11C8102731CFD700AC7318 /* ReaderModeStyleTests.swift */, 03CCC9171AF05E7300DBF30D /* RelativeDatesTests.swift */, F80D53CD2A09A30F0047ED14 /* RustSyncManagerTests.swift */, + 8A5189C62C1B5F6C00CDB668 /* Search */, 8A5D1CA12A30CF2E005AD35C /* Settings */, 8C6DA7D02A6FE78F00DE264F /* ShoppingProductTests.swift */, 8A04136A2825ABEA00D20B10 /* SponsoredTileTelemetryTests.swift */, @@ -12529,12 +12845,15 @@ D815A3A724A53F3200AAB221 /* TabToolbarHelperTests.swift */, 21D8EA942ABE0511003FF16E /* TabTray */, 8A6E13972A71BA4E00A88FA8 /* TabWebViewTests.swift */, + 8AC225632B6D3F9600CDA7FD /* Telemetry */, 8A95FF652B1E977E00AC303D /* TelemetryContextualIdentifierTests.swift */, 8AA6ADB42742B567004EEE23 /* TelemetryWrapperTests.swift */, 0BA8964A1A250E6500C1010C /* TestBookmarks.swift */, 4A59BF410BBD9B3BE71F4C7C /* TestHistory.swift */, 8A33222027DFE64C008F809E /* TestingHelperClasses */, + E16941B22C4E498600FF5F4E /* Toolbar */, E1D8BC7921FF7A0000B100BD /* TPStatsBlocklistsTests.swift */, + AB9CBC0A2C53B9A400102610 /* TrackingProtectionTests */, ABB507CD2A136FB2009CAA67 /* UserConversionMetricsTests.swift */, 8A1E3BE028CBAC1F003388C4 /* Utils */, C84655ED28873C4800861B4A /* Wallpaper */, @@ -12543,11 +12862,6 @@ 1D74FF4D2B27962200FF01D0 /* WindowManagerTests.swift */, D3BA41671BD82F2200DA5457 /* XCTestCaseExtensions.swift */, 8A96C4BA28F9E7B300B75884 /* XCTestCaseRootViewController.swift */, - 8A5189C62C1B5F6C00CDB668 /* Search */, - 814A62452B587A3E00608195 /* DefaultThemeManagerTests.swift */, - E1E425312B5A2E9700899550 /* DownloadTests.swift */, - 0AC659262BF35854005C614A /* FxAWebViewModelTests.swift */, - C787D8C22C1CB77900940123 /* FirefoxAccountSignInViewControllerTests.swift */, ); path = ClientTests; sourceTree = ""; @@ -13543,6 +13857,7 @@ 431F0C4A2AC1A112006D7D49 /* TabLocation.strings in Resources */, 43903A8F2C20502A00ACF76E /* EnhancedTrackingProtection.strings in Resources */, 43A878172B838FF90039D6B7 /* PasswordAutofill.strings in Resources */, + 43E6BE162C57AF7600C2152C /* Toolbar.strings in Resources */, D59643E725C9B8E000EAB8B9 /* Intro.strings in Resources */, D59643F325C9B8E000EAB8B9 /* Storage.strings in Resources */, D59643E925C9B8E000EAB8B9 /* Menu.strings in Resources */, @@ -13586,6 +13901,7 @@ 4308A4E62A52E38C001D652E /* SelectCreditCard.strings in Resources */, 43FA499D29C875C0005062DB /* EditCard.strings in Resources */, 43B627762C3C001E00A188E8 /* ScanQRCode.strings in Resources */, + 437AE3102C7C9A0C0053F7F3 /* NativeErrorPage.strings in Resources */, 433BADA029C8769800E34991 /* BiometricAuthentication.strings in Resources */, D58A202F25C9D96400105D25 /* BookmarkPanelDeleteConfirm.strings in Resources */, D59643EA25C9B8E000EAB8B9 /* HistoryPanel.strings in Resources */, @@ -13909,7 +14225,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if [ $ACTION != \"indexbuild\" ]; then\n bash $SOURCE_ROOT/bin/nimbus-fml.sh --verbose\nfi\n"; + shellScript = "if [ $ACTION != \"indexbuild\" ]; then\n /usr/bin/env -i HOME=$HOME PROJECT=$PROJECT CONFIGURATION=$CONFIGURATION SOURCE_ROOT=$SOURCE_ROOT bash $SOURCE_ROOT/bin/nimbus-fml.sh --verbose\nfi\n"; }; C874A4E327F62C5B006F54E5 /* Swiftlint */ = { isa = PBXShellScriptBuildPhase; @@ -14213,6 +14529,7 @@ 0B3D670E1E09B90B00C1EFC7 /* AuthenticationTest.swift in Sources */, D4C4BDCE2253725E00986F04 /* LibraryTests.swift in Sources */, 2CCF17532105E4FD00705AE5 /* DisplaySettingsTests.swift in Sources */, + B1CA62822C0DB43600D31625 /* MultiWindowTests.swift in Sources */, 2C97EC711E72C80E0092EC18 /* TopTabsTest.swift in Sources */, 39EB469A1E26DDB4006346E8 /* FxScreenGraph.swift in Sources */, 2CC1B3F01E9B861400814EEC /* DomainAutocompleteTests.swift in Sources */, @@ -14419,7 +14736,6 @@ CA8226F324C11DB7008A6F38 /* PasswordManagerTableViewCell.swift in Sources */, 8A0A1BA02B2200FD00E8706F /* PrivateHomepageViewController.swift in Sources */, E4A960061ABB9C450069AD6F /* ReaderModeUtils.swift in Sources */, - E17798962BD6B33300F6F0EB /* ToolbarFlagManager.swift in Sources */, 8AD40FCB27BADC4B00672675 /* StatefulButton.swift in Sources */, C84655E62887398700861B4A /* WallpaperCollection.swift in Sources */, EBF47E701F7979DF00899189 /* TelemetryWrapper.swift in Sources */, @@ -14490,6 +14806,7 @@ D3C3696E1CC6B78800348A61 /* LocalRequestHelper.swift in Sources */, E17496382991A2720096900A /* AdaptiveStack.swift in Sources */, E4B423DD1ABA0318007E66C8 /* ReaderModeHandlers.swift in Sources */, + 7A6DF9EB2BFECC3C00A0C608 /* LegacyTabPeekPreviewActionBuilder.swift in Sources */, E177989C2BD7D48500F6F0EB /* ToolbarAction.swift in Sources */, F8A0B08229AD61FA0091C75B /* RustSyncManager.swift in Sources */, D308E4E41A5306F500842685 /* SearchEngines.swift in Sources */, @@ -14526,6 +14843,7 @@ EBFDB790211C83A5005CCA2F /* BrowserViewController+FindInPage.swift in Sources */, 21B359C62AEAC20300FF09E3 /* TabsSectionManager.swift in Sources */, 21BFEEF52A040EF40033048D /* TabMigrationUtility.swift in Sources */, + 8AEAD9F52C3D7BA9001A2C5A /* FeatureFlagsDebugViewController.swift in Sources */, A55319BB2B5D5A850051559F /* SearchSettingsAction.swift in Sources */, 810CD9C12BB346D800E290C2 /* OnboardingCardViewController.swift in Sources */, C8A012F126AB07D70096A7A7 /* JumpBackInViewModel.swift in Sources */, @@ -14623,6 +14941,7 @@ B2DFB7E32B619E2B0004CEA5 /* AddressCellView.swift in Sources */, AB6FEA202AEA5CA200E7B2F2 /* FakespotAdView.swift in Sources */, C87A121A28C2451A0097ED51 /* WallpaperMigrationUtility.swift in Sources */, + AB9CBC032C53B64C00102610 /* TrackingProtectionMiddleware.swift in Sources */, D87F84AC20B891160091F2DA /* TabDisplayManager.swift in Sources */, D0C95EF6201A55A800E4E51C /* BrowserViewController+UIDropInteractionDelegate.swift in Sources */, D31CF65C1CC1959A001D0BD0 /* PrivilegedRequest.swift in Sources */, @@ -14638,6 +14957,7 @@ D3B6923D1B9F9444004B87A4 /* FindInPageBar.swift in Sources */, 1D5CBF492B17E3CB0001D033 /* NotificationPayloads.swift in Sources */, 2F44FC721A9E840300FD20CC /* SettingsNavigationController.swift in Sources */, + AB9CBC052C53B64C00102610 /* TrackingProtectionState.swift in Sources */, D0E89A2920910917001CE5C7 /* DownloadsPanel.swift in Sources */, D3BA7E0E1B0E934F00153782 /* ContextMenuHelper.swift in Sources */, E19B38B328A42D5E00D8C541 /* WallpaperCollectionViewCell.swift in Sources */, @@ -14740,10 +15060,12 @@ 964FA97528A1A8F20024BB3B /* ContextualHintEligibilityUtility.swift in Sources */, 439C489C29760575007C3DCD /* CreditCardValidator.swift in Sources */, 8ADC2A122A3375B900543DAA /* FxAEntryPoint.swift in Sources */, + 8AEAD9F32C3D7B3E001A2C5A /* FeatureFlagsSettings.swift in Sources */, 216A0D7B2A40F08B008077BA /* ThemeSettingsAction.swift in Sources */, C2506C932A6A863600F2B76E /* HistoryCoordinator.swift in Sources */, 8A3EF8012A2FCFC900796E3A /* FasterInactiveTabs.swift in Sources */, CA7FC7D324A6A9B70012F347 /* PasswordManagerDataSourceHelper.swift in Sources */, + 8A5BC1EB2C4AA13F009A40A4 /* FeatureFlagsBoolSetting.swift in Sources */, 43AB6FA425DC53D30016B015 /* LabelButtonHeaderView.swift in Sources */, 43D16B7C29831CD0009F8279 /* CreditCardItemRow.swift in Sources */, 39673BC12B6D82F400653F4A /* FxNimbusMessaging.swift in Sources */, @@ -14846,6 +15168,7 @@ 3BB50E201D627539004B33DF /* HomepageViewController.swift in Sources */, C82A94F3269F68F300624AA7 /* CoreFlaggableFeature.swift in Sources */, F84B22241A09122500AAB793 /* LibraryViewController.swift in Sources */, + E16941B82C5119A200FF5F4E /* Autocompletable.swift in Sources */, 39455F771FC83F430088A22C /* TabEventHandler.swift in Sources */, 8A36BE2929EDBC6900AC1C5C /* ContentContainer.swift in Sources */, 215B458227DA420400E5E800 /* LegacyTabMetadataManager.swift in Sources */, @@ -14854,6 +15177,7 @@ EBB89507219398E500EB91A0 /* ContentBlocker.swift in Sources */, 216A0D792A40E85A008077BA /* ThemeSettingsState.swift in Sources */, 5A3A2A0D287F742C00B79EAC /* BackgroundSyncUtility.swift in Sources */, + 8AABB92D2C64F77000F1FE51 /* LoginProvider.swift in Sources */, 21AFCFEE2AE80B700027E9CE /* TabsCoordinator.swift in Sources */, 8A1CBB952BE017D3008BE4D4 /* MicrosurveyPromptAction.swift in Sources */, 23ED80FF25C89C9800D0E9D5 /* DefaultBrowserOnboardingViewController.swift in Sources */, @@ -14875,6 +15199,7 @@ E13C072B2C2184C80087E404 /* AddressBarState.swift in Sources */, 4347B39A298DA5BB0045F677 /* CreditCardInputViewModel.swift in Sources */, 6025B10D267B6C5400F59F6B /* LoginRecordExtension.swift in Sources */, + 8AB893A62C73AFBA00DAEED7 /* MockLoginProvider.swift in Sources */, 2F44FCC51A9E85E900FD20CC /* SettingsTableViewController.swift in Sources */, 211046C92A7ADE9000A7309F /* BlockPopupSetting.swift in Sources */, 8A3233FC286270CF003E1C33 /* FxBookmarkNode.swift in Sources */, @@ -14883,9 +15208,11 @@ 8A83B7462A264FA0002FF9AC /* SettingsCoordinator.swift in Sources */, A9072B801D07B34100459960 /* NoImageModeHelper.swift in Sources */, 433396C827ACE92500491049 /* LegacyInactiveTabButton.swift in Sources */, + 8A97E6EA2C58487900F94793 /* AddressLocaleFeatureValidator.swift in Sources */, AB3DB0C92B596739001D32CB /* AppStartupTelemetry.swift in Sources */, D8AA923421A602DC002605C0 /* HomePageSettingViewController.swift in Sources */, 8A161411282C035D00DDBB02 /* CustomizeHomepageSectionViewModel.swift in Sources */, + AB9CBC042C53B64C00102610 /* TrackingProtectionModel.swift in Sources */, C8DC90C52A066B6A0008832B /* MarkupTokenizingUtility.swift in Sources */, EBC4869D2195F58300CDA48D /* ErrorPageHelper.swift in Sources */, C84655E8288739CB00861B4A /* WallpaperCollectionAvailability.swift in Sources */, @@ -14926,6 +15253,7 @@ 8A1E93EA2A3CDC6100DD540A /* BaseCoordinator.swift in Sources */, E1442FD6294782D9003680B0 /* UIView+Extension.swift in Sources */, A5519CF52B5D57560062BECB /* SearchSettingsState.swift in Sources */, + AB936A692C05F2B100600F82 /* TrackingProtectionButton.swift in Sources */, 74F80D342A0A52D700013C3D /* PrivacyPolicyViewController.swift in Sources */, 274A36CE239EB9EC00A21587 /* LibraryViewController+LibraryPanelDelegate.swift in Sources */, C869912D28917688007ACC5C /* WallpaperImageLoader.swift in Sources */, @@ -14951,6 +15279,7 @@ AB03032C2AB47AF300DCD8EF /* FakespotOptInCardViewModel.swift in Sources */, 1D2F68AB2ACB262900524B92 /* RemoteTabsPanelAction.swift in Sources */, 8A5D1CB92A30DBDB005AD35C /* ChinaSyncServiceSetting.swift in Sources */, + 8AB893A92C73CBBD00DAEED7 /* CreditCardProvider.swift in Sources */, 1D8487B42AD0C6C100F7527C /* RemoteTabsPanelMiddleware.swift in Sources */, 5AA1D8272BC09ECB00957516 /* TabTrayAnimationQueue.swift in Sources */, C849E46526B9C3DD00260F0B /* SlideoverPresentationController.swift in Sources */, @@ -14962,6 +15291,7 @@ 2165B2C22860C2F4004C0786 /* AdjustTelemetryHelper.swift in Sources */, 8A2D593E27DC0AA100713EC9 /* TopSite.swift in Sources */, 8A0A1BA32B22030100E8706F /* PrivateMessageCardCell.swift in Sources */, + AB9CBC022C53B64C00102610 /* TrackingProtectionAction.swift in Sources */, 213BF7532AC21D1B00C53A64 /* TabDisplayPanel.swift in Sources */, 434E733725EED32E006D3BDE /* BrowserViewController+URLBarDelegate.swift in Sources */, C88E7A5B2A0553510072E638 /* OnboardingLinkInfoModel.swift in Sources */, @@ -15077,6 +15407,7 @@ D3C744CD1A687D6C004CE85D /* URIFixup.swift in Sources */, 43D16B8029831DC5009F8279 /* CreditCardInputView.swift in Sources */, 21112968289480630082C08B /* HomepageMessageCardViewModel.swift in Sources */, + 8AB893A32C73AF5200DAEED7 /* MockCreditCardProvider.swift in Sources */, E1CD81BE290C5C7500124B27 /* DevicePickerTableViewHeaderCell.swift in Sources */, B2FEA68B2B460D1D0058E616 /* AddressAutofillSettingsView.swift in Sources */, E12BD0AC28AC37F00029AAF0 /* UIColor+Extension.swift in Sources */, @@ -15105,6 +15436,7 @@ 4393932029AC6CE900DC5A85 /* EnvironmentValues+Extension.swift in Sources */, ABEF80D12A24D2BE003F52C4 /* CreditCardBottomSheetViewModel.swift in Sources */, 21AFCFF02AE80D370027E9CE /* RemoteTabsCoordinator.swift in Sources */, + 7ADC1D192C27D35B003ED924 /* ActionProviderBuilder.swift in Sources */, 43162A2F2492DB7800F91658 /* EmptyPrivateTabsView.swift in Sources */, E1380B8D2AEA897C00630AFA /* SidebarEnabledView.swift in Sources */, D0625C98208E87F10081F3B2 /* DownloadQueue.swift in Sources */, @@ -15184,6 +15516,7 @@ C849E46126B9C39B00260F0B /* EnhancedTrackingProtectionVC.swift in Sources */, E40FAB0C1A7ABB77009CB80D /* WebServer.swift in Sources */, 59A68D66379CFA85C4EAF00B /* TwoLineImageOverlayCell.swift in Sources */, + 0A49784A2C53E63200B1E82A /* TrackingProtectionViewController.swift in Sources */, 8A04136928258DF600D20B10 /* SponsoredTileTelemetry.swift in Sources */, D04CD718215EBD85004FF5B0 /* SettingsLoadingView.swift in Sources */, 21420EF72ABA338D00B28550 /* TabTrayCoordinator.swift in Sources */, @@ -15251,6 +15584,7 @@ 03CCC9181AF05E7300DBF30D /* RelativeDatesTests.swift in Sources */, F84B21DA1A090F8100AAB793 /* ClientTests.swift in Sources */, 219935F12B07DFA200E5966F /* TabDisplayPanelTests.swift in Sources */, + E16941B42C4E4A2E00FF5F4E /* BrowserViewControllerStateTests.swift in Sources */, E1AEC179286E0CF500062E29 /* FirefoxHomeViewModelTests.swift in Sources */, DF940A0C2A96352B00C1497D /* FakespotSettingsCardViewModelTests.swift in Sources */, 8ABCFEA62B45CB4C00C2988A /* PrivateBrowsingTelemetryTests.swift in Sources */, @@ -15272,6 +15606,7 @@ 8A8482F02BE1602500F9007B /* MicrosurveyPromptStateTests.swift in Sources */, 8ADED7EC27691351009C19E6 /* CalendarExtensionsTests.swift in Sources */, 3B39EDBA1E16E18900EF029F /* CustomSearchEnginesTest.swift in Sources */, + 0AFF7F662C7784F100265214 /* TrackingProtectionModelTests.swift in Sources */, C80C11EE28B3C8B80062922A /* WallpaperMetadataTrackerTests.swift in Sources */, 8C8D8C7A2AA067AD00490D32 /* FakespotCoordinatorTests.swift in Sources */, C8E531CA29E5F7D300E03FEF /* URLScannerTests.swift in Sources */, @@ -15412,6 +15747,7 @@ 0BA8964C1A250E6500C1010C /* TestBookmarks.swift in Sources */, 8AF3B15C2AF99C77009BB262 /* ReadingListPanelTests.swift in Sources */, 8AF99B5429EF2AF100108DEC /* MockLogger.swift in Sources */, + AB9CBC082C53B7CD00102610 /* TrackingProtectionStateTests.swift in Sources */, 5A9A09D428B01D8700B6F51E /* MockTelemetryWrapper.swift in Sources */, C80C11F028B3C9150062922A /* MockUserDefaults.swift in Sources */, D8BA1790206D47830023AC00 /* DeferredTestUtils.swift in Sources */, @@ -15452,6 +15788,7 @@ D525DFB325FBE5E000B18763 /* TabTests.swift in Sources */, C8E78BDD27F4A1E700C48BAA /* HistoryDeletionUtilityTests.swift in Sources */, 0AC659272BF35854005C614A /* FxAWebViewModelTests.swift in Sources */, + 8AB893A72C73AFCC00DAEED7 /* MockLoginViewModelDelegate.swift in Sources */, 8A11C8132731E54800AC7318 /* DictionaryExtensionsTests.swift in Sources */, 5A475E8F29DB89CE009C13FD /* MockTabDataStore.swift in Sources */, 8A7A93EE2810ADF2005E7E1B /* ContileProviderTests.swift in Sources */, @@ -15477,6 +15814,7 @@ 4A59B58AD11B5EE1F80BBDEB /* TestHistory.swift in Sources */, A83E5B1D1C1DA8D80026D912 /* UIPasteboardExtensionsTests.swift in Sources */, 8A635ECD289437A8006378BA /* SyncedTabCellTests.swift in Sources */, + 8A97E6EF2C584C4900F94793 /* AddressLocaleFeatureValidatorTests.swift in Sources */, E1E425322B5A2E9700899550 /* DownloadTests.swift in Sources */, 5AB4237C28A1947A003BC40C /* MockNotificationCenter.swift in Sources */, 8A37C79F28DA4BA600B1FAD4 /* ContextualHintViewProviderTests.swift in Sources */, @@ -15486,6 +15824,7 @@ 8AFE4C2127480D0C00B97C65 /* LegacyTabTrayViewControllerTests.swift in Sources */, E1312FD129D237EE008DDA85 /* NotificationSurfaceManagerTests.swift in Sources */, 21371FA228A6C4A200BC3F37 /* OnboardingTelemetryUtilityTests.swift in Sources */, + 7A352B772C4F196B00359D51 /* LegacyTabPeekPreviewActionBuilderTests.swift in Sources */, 5AE371842A4DD6F50092A760 /* PasswordManagerListViewControllerSpy.swift in Sources */, 8A2825352760399B00395E66 /* KeyboardPressesHandlerTests.swift in Sources */, ABEF80D92A2F283E003F52C4 /* CreditCardBottomSheetViewModelTests.swift in Sources */, @@ -15495,6 +15834,7 @@ 21FA8FAE2AE856460013B815 /* TabsCoordinatorTests.swift in Sources */, E1D8BC7A21FF7A0000B100BD /* TPStatsBlocklistsTests.swift in Sources */, 5AF6254328A57A4600A90253 /* HistoryHighlightsDataAdaptorTests.swift in Sources */, + 0AFF7F642C7784D600265214 /* MockDataCleaner.swift in Sources */, 8AE80BAD2891957C00BC12EA /* TopSitesDimensionTests.swift in Sources */, D82ED2641FEB3C420059570B /* DefaultSearchPrefsTests.swift in Sources */, 1D74FF502B2797EA00FF01D0 /* WindowManagerTests.swift in Sources */, @@ -16207,6 +16547,7 @@ 439CB65D2AE68F1F00A19D54 /* sq */, 4364EA952B1DE9FB003A1240 /* bs */, 43EB5FA72B2728080063D23E /* ug */, + 4300B0DE2C57B06E00F609C1 /* es-MX */, ); name = SelectCreditCard.strings; sourceTree = ""; @@ -16272,6 +16613,8 @@ 43CCB1A22B27249B00D6B1C0 /* br */, 43EB5FAC2B2728080063D23E /* ug */, 439E22242C04A5620061A923 /* su */, + 4300B0DF2C57B06E00F609C1 /* es-MX */, + 43E0BE412C60EC4C004FED65 /* hr */, ); name = TabLocation.strings; sourceTree = ""; @@ -16334,6 +16677,7 @@ 439E22202C04A5610061A923 /* su */, 43CCB4D42C32C527001F2EBB /* ca */, 43EC79AA2C3C00BF00519D98 /* es-MX */, + 43E0BE3C2C60EC4C004FED65 /* hr */, ); name = KeyboardAccessory.strings; sourceTree = ""; @@ -16402,6 +16746,7 @@ 43D2F8B72A8A45570095D4EB /* su */, 4364EA842B1DE9FA003A1240 /* bs */, 43EB5F982B2728070063D23E /* ug */, + 43E0BE342C60EC4B004FED65 /* hr */, ); name = BiometricAuthentication.strings; sourceTree = ""; @@ -16536,6 +16881,7 @@ 43EB5FAA2B2728080063D23E /* ug */, 4357B4942C168FF1003518CF /* su */, 43EC79AE2C3C00BF00519D98 /* es-MX */, + 43E0BE402C60EC4C004FED65 /* hr */, ); name = Shopping.strings; sourceTree = ""; @@ -16605,6 +16951,7 @@ 4397616F2A0BCB850062C60C /* gl */, 4364EA8F2B1DE9FB003A1240 /* bs */, 43EB5FA12B2728080063D23E /* ug */, + 43E0BE3B2C60EC4C004FED65 /* hr */, ); name = JumpBackIn.strings; sourceTree = ""; @@ -16674,6 +17021,7 @@ 439761772A0BCB860062C60C /* gl */, 4364EA9C2B1DE9FB003A1240 /* bs */, 43EB5FAE2B2728080063D23E /* ug */, + 43C9CD4A2C6A26D9002F86B4 /* hr */, ); name = ToolbarLocation.strings; sourceTree = ""; @@ -16739,6 +17087,7 @@ 432792682BB192FD001707B5 /* en-CA */, 439E221E2C04A5610061A923 /* su */, 43B890FF2C32C60B0049F033 /* es-MX */, + 43E0BE392C60EC4B004FED65 /* hr */, ); name = FirefoxHomepage.strings; sourceTree = ""; @@ -16802,6 +17151,8 @@ 43553E932C298AA30068B484 /* br */, 43CCB4CE2C32C526001F2EBB /* ca */, 43B890F92C32C60A0049F033 /* es-MX */, + 4361DF292C453DB200CE1295 /* sat-Olck */, + 43E0BE352C60EC4B004FED65 /* hr */, ); name = BottomSheet.strings; sourceTree = ""; @@ -17017,6 +17368,49 @@ name = TabsTray.strings; sourceTree = ""; }; + 437AE30E2C7C9A0C0053F7F3 /* NativeErrorPage.strings */ = { + isa = PBXVariantGroup; + children = ( + 437AE30F2C7C9A0C0053F7F3 /* bs */, + 43CFB14C2C7C9A2B003B700F /* co */, + 43DE88092C7C9A3B00A7D4E0 /* cs */, + 43EC361F2C7C9A4B00824F5E /* cy */, + 4345E88F2C7C9A5B00FC0D7D /* da */, + 43548AB92C7C9A6A0004C846 /* de */, + 437CD9FF2C7C9A8B00A34E3E /* el */, + 43E1DCD82C7C9AAB008E952F /* en-GB */, + 43F013FF2C7C9ABA00F8CF52 /* en-US */, + 43FECBDB2C7C9AC900D644AE /* eo */, + 4364018E2C7C9AE700FF64C6 /* es-AR */, + 4373440E2C7C9AF500457D4D /* es-CL */, + 430EC8982C7C9B5100759193 /* fi */, + 431D8FEE2C7C9B61001E041F /* fr */, + 43ADBF222C7C9BAF006B6E34 /* he */, + 4321F0522C7C9BE00001D0DD /* hsb */, + 43940EE22C7C9C0D000AA60B /* ia */, + 43B0B5692C7C9C2A00B2A5AE /* is */, + 43BF3C5F2C7C9C380017A926 /* it */, + 43C4422D2C7C9C4700ED0870 /* ja */, + 43400BC22C7C9C7100D8ECE9 /* kab */, + 434FE6142C7C9C7F00C44560 /* kk */, + 43EE827C2C7C9D4600CDF5B9 /* nn */, + 4361916A2C7C9D72005B31E3 /* pa-IN */, + 436FC4912C7C9D810083D324 /* pl */, + 437E27022C7C9D8F00CE7490 /* pt-BR */, + 438C433E2C7C9D9F005AB958 /* pt-PT */, + 4304BD432C7C9DCB0063AF05 /* ru */, + 438613FD2C7C9E05005E3A65 /* sk */, + 439410F22C7C9E1400328C59 /* sl */, + 430832542C7C9E4100F58087 /* sv */, + 43964FF82C7C9E89001C730A /* tr */, + 43B210482C7C9EA500B136AD /* ug */, + 4334C4F02C7C9EDE00FBEFCF /* vi */, + 43500A6B2C7C9EFB008E385F /* zh-CN */, + 43CF316F2C7C9F0900D898EE /* zh-TW */, + ); + name = NativeErrorPage.strings; + sourceTree = ""; + }; 43903A872C20502A00ACF76E /* ActivityStream.strings */ = { isa = PBXVariantGroup; children = ( @@ -17071,6 +17465,12 @@ 434E0D022C32C8B60098A594 /* sat-Olck */, 43724B3B2C32C8D200AB7D3D /* si */, 430901D52C3C00DB00D5F3AC /* eu */, + 431B95F62C453A89003B17BF /* en-CA */, + 4375F0E12C453BC4001F3A55 /* hu */, + 4300B0D92C57B06E00F609C1 /* es-MX */, + 437E67412C57B4AB00A8791A /* th */, + 43E0BE322C60EC4B004FED65 /* hr */, + 43C09A7A2C7C9EB400BCD71D /* uk */, ); name = ActivityStream.strings; sourceTree = ""; @@ -17128,6 +17528,11 @@ 434E0D032C32C8B60098A594 /* sat-Olck */, 43724B3C2C32C8D200AB7D3D /* si */, 430901D62C3C00DB00D5F3AC /* eu */, + 431B95F72C453A89003B17BF /* en-CA */, + 4375F0E22C453BC4001F3A55 /* hu */, + 437E67422C57B4AB00A8791A /* th */, + 43E0BE332C60EC4B004FED65 /* hr */, + 43C09A7B2C7C9EB400BCD71D /* uk */, ); name = AddressToolbar.strings; sourceTree = ""; @@ -17185,6 +17590,12 @@ 43B3F3382C32C863006BA8E5 /* pl */, 43724B3D2C32C8D200AB7D3D /* si */, 430901D82C3C00DB00D5F3AC /* eu */, + 431B95F92C453A89003B17BF /* en-CA */, + 4375F0E42C453BC4001F3A55 /* hu */, + 4300B0DB2C57B06E00F609C1 /* es-MX */, + 437E67442C57B4AB00A8791A /* th */, + 43E0BE382C60EC4B004FED65 /* hr */, + 43C09A7D2C7C9EB400BCD71D /* uk */, ); name = EnhancedTrackingProtection.strings; sourceTree = ""; @@ -17240,6 +17651,10 @@ 43D812902C32C8EE00CC6AB0 /* sl */, 430901D92C3C00DB00D5F3AC /* eu */, 43B5176E2C3C0217004F9302 /* kab */, + 431B95FA2C453A89003B17BF /* en-CA */, + 4375F0E52C453BC4001F3A55 /* hu */, + 437E67452C57B4AB00A8791A /* th */, + 43C9CD452C6A26D9002F86B4 /* hr */, ); name = QRCode.strings; sourceTree = ""; @@ -17450,6 +17865,7 @@ 4364EA932B1DE9FB003A1240 /* bs */, 43EB5FA52B2728080063D23E /* ug */, 437E34352C3C03870025BFA9 /* ro */, + 43C9CD462C6A26D9002F86B4 /* hr */, ); name = ResearchSurface.strings; sourceTree = ""; @@ -17518,6 +17934,7 @@ 4364EA902B1DE9FB003A1240 /* bs */, 43EB5FA22B2728080063D23E /* ug */, 439E22232C04A5620061A923 /* su */, + 43E0BE3F2C60EC4C004FED65 /* hr */, ); name = Notification.strings; sourceTree = ""; @@ -17586,6 +18003,7 @@ 439CB65F2AE68F1F00A19D54 /* sq */, 4364EA9F2B1DE9FB003A1240 /* bs */, 43EB5FB12B2728090063D23E /* ug */, + 43E0BE432C60EC4C004FED65 /* hr */, ); name = ZoomPageBar.strings; sourceTree = ""; @@ -17793,6 +18211,7 @@ 4301323E2ADD559F00B1FAD7 /* sq */, 4364EA9D2B1DE9FB003A1240 /* bs */, 43EB5FAF2B2728080063D23E /* ug */, + 4300B0E12C57B06E00F609C1 /* es-MX */, ); name = UpdateCard.strings; sourceTree = ""; @@ -17855,6 +18274,7 @@ 4357B4922C168FF1003518CF /* su */, 43CCB4D72C32C527001F2EBB /* ca */, 43EC79AB2C3C00BF00519D98 /* es-MX */, + 43C9CD442C6A26D9002F86B4 /* hr */, ); name = PasswordAutofill.strings; sourceTree = ""; @@ -17918,6 +18338,9 @@ 439E221B2C04A5610061A923 /* su */, 43CCB4CF2C32C526001F2EBB /* ca */, 43B890FA2C32C60A0049F033 /* es-MX */, + 43D79A832C4539FD0057C720 /* br */, + 4361DF2A2C453DB200CE1295 /* sat-Olck */, + 43C9CD422C6A26D8002F86B4 /* hr */, ); name = ContextualHints.strings; sourceTree = ""; @@ -17983,6 +18406,8 @@ 439E221C2C04A5610061A923 /* su */, 43CCB4D02C32C526001F2EBB /* ca */, 43B890FB2C32C60A0049F033 /* es-MX */, + 4361DF2B2C453DB200CE1295 /* sat-Olck */, + 43E0BE362C60EC4B004FED65 /* hr */, ); name = CredentialProvider.strings; sourceTree = ""; @@ -18045,6 +18470,8 @@ 439E221D2C04A5610061A923 /* su */, 43CCB4D12C32C526001F2EBB /* ca */, 43B890FC2C32C60B0049F033 /* es-MX */, + 433020F72C57B40600AF5EE2 /* sat-Olck */, + 43C9CD432C6A26D9002F86B4 /* hr */, ); name = Credentials.strings; sourceTree = ""; @@ -18110,6 +18537,7 @@ 439E221F2C04A5610061A923 /* su */, 43CCB4D32C32C527001F2EBB /* ca */, 43B891002C32C60B0049F033 /* es-MX */, + 43E0BE3A2C60EC4C004FED65 /* hr */, ); name = FirefoxLogins.strings; sourceTree = ""; @@ -18175,6 +18603,7 @@ 4300B70C2C298D9C0099DEEC /* lo */, 43CCB4D52C32C527001F2EBB /* ca */, 43B891022C32C60B0049F033 /* es-MX */, + 43E0BE3D2C60EC4C004FED65 /* hr */, ); name = LoginsHelper.strings; sourceTree = ""; @@ -18236,6 +18665,7 @@ 43F851162C168FBB00FA74A7 /* si */, 4357B4952C168FF1003518CF /* su */, 43CCB4D92C32C527001F2EBB /* ca */, + 43C9CD492C6A26D9002F86B4 /* hr */, ); name = TabToolbar.strings; sourceTree = ""; @@ -18272,6 +18702,30 @@ 433D51752C3C0478005D19E3 /* ug */, 436AFFF52C3C04D300CDBA3C /* zh-CN */, 4333C7392C3C04E200B4226E /* zh-TW */, + 43D79A852C4539FD0057C720 /* br */, + 43F362722C453A1900F14754 /* ca */, + 430D6E552C453A7B003ADD09 /* el */, + 431B95FB2C453A8A003B17BF /* en-CA */, + 435538172C453B3900DD79B0 /* fi */, + 4375F0E62C453BC4001F3A55 /* hu */, + 43B74E662C453D5000CDBC34 /* pa-IN */, + 4338F50D2C453D8800851075 /* rm */, + 43F6E7632C453DF70018EEA4 /* sq */, + 4383E28B2C453EAA00B775A3 /* vi */, + 43E6BE132C57AF7600C2152C /* bs */, + 43383DEA2C57AF94001BCD0E /* co */, + 435B35652C57AFA200F1C0BE /* cs */, + 436957A82C57AFB100B079FE /* cy */, + 4378EFA82C57AFBF0031897A /* da */, + 4300B0DD2C57B06E00F609C1 /* es-MX */, + 4390F7E22C57B1E700F52D3C /* ja */, + 438B16432C57B39C007BD1A3 /* pl */, + 43B3869C2C57B446007728B6 /* sl */, + 437E67462C57B4AB00A8791A /* th */, + 433D64C62C60EE1100CEC389 /* nb */, + 43C9CD472C6A26D9002F86B4 /* hr */, + 43FECBDC2C7C9ACA00D644AE /* eo */, + 43C09A7E2C7C9EB400BCD71D /* uk */, ); name = ScanQRCode.strings; sourceTree = ""; @@ -18333,6 +18787,8 @@ 438ABE532C0DDB6C00528A07 /* br */, 43B68F092C1690270012DAE2 /* th */, 43CCB4D62C32C527001F2EBB /* ca */, + 4300B0DC2C57B06E00F609C1 /* es-MX */, + 43E0BE3E2C60EC4C004FED65 /* hr */, ); name = Microsurvey.strings; sourceTree = ""; @@ -18516,6 +18972,26 @@ 43BFD4502C3C04B400B76DE5 /* vi */, 436AFFF42C3C04D300CDBA3C /* zh-CN */, 4333C7382C3C04E200B4226E /* zh-TW */, + 43D79A842C4539FD0057C720 /* br */, + 43F362712C453A1900F14754 /* ca */, + 43590D282C453A350085A1D2 /* cs */, + 438CC1982C453A43000BC4CF /* cy */, + 431B95F82C453A89003B17BF /* en-CA */, + 4375F0E32C453BC4001F3A55 /* hu */, + 4338F50C2C453D8800851075 /* rm */, + 43F6E7622C453DF70018EEA4 /* sq */, + 43383DE92C57AF94001BCD0E /* co */, + 4378EFA72C57AFBF0031897A /* da */, + 4300B0DA2C57B06E00F609C1 /* es-MX */, + 4390F7E12C57B1E700F52D3C /* ja */, + 438E812A2C57B30F004E1492 /* nb */, + 438B16422C57B39C007BD1A3 /* pl */, + 433020F82C57B40700AF5EE2 /* sat-Olck */, + 4396C29A2C57B42700837488 /* si */, + 437E67432C57B4AB00A8791A /* th */, + 43E0BE372C60EC4B004FED65 /* hr */, + 43FECBDA2C7C9AC900D644AE /* eo */, + 43C09A7C2C7C9EB400BCD71D /* uk */, ); name = EditAddress.strings; sourceTree = ""; @@ -18581,6 +19057,7 @@ 43D74AE12BCD4286000D0251 /* en-CA */, 4357B4932C168FF1003518CF /* su */, 43EC79AD2C3C00BF00519D98 /* es-MX */, + 43C9CD482C6A26D9002F86B4 /* hr */, ); name = Share.strings; sourceTree = ""; @@ -18725,6 +19202,65 @@ name = SearchHeaderTitle.strings; sourceTree = ""; }; + 43E6BE142C57AF7600C2152C /* Toolbar.strings */ = { + isa = PBXVariantGroup; + children = ( + 43E6BE152C57AF7600C2152C /* bs */, + 43383DEB2C57AF94001BCD0E /* co */, + 435B35662C57AFA200F1C0BE /* cs */, + 4378EFA92C57AFBF0031897A /* da */, + 4386E32C2C57AFCF00C97DCD /* de */, + 43DF2A222C57AFDD0083E728 /* dsb */, + 43ED215A2C57AFEC0014F10F /* el */, + 43FB540B2C57AFFA00562B25 /* en-CA */, + 430ADCC62C57B00800B0A55B /* en-GB */, + 431813AE2C57B017007C3268 /* en-US */, + 438C39022C57B042005CD2CC /* es-AR */, + 439A03F62C57B051003675A3 /* es-CL */, + 4300B0E02C57B06E00F609C1 /* es-MX */, + 433954F62C57B0A900EC1EAE /* fi */, + 4391326B2C57B0BB00805AE1 /* fr */, + 43211E6B2C57B11F003F9FAF /* he */, + 43961E7E2C57B15B00762948 /* hsb */, + 43B388562C57B1830004C424 /* hy-AM */, + 430B81E02C57B198008C994B /* ia */, + 4329BDC32C57B1BF007F18C7 /* is */, + 434ED7EC2C57B1D4000C86E4 /* it */, + 4390F7E32C57B1E800F52D3C /* ja */, + 437A0B5A2C57B22500FCF751 /* kab */, + 4315028A2C57B23B005BB18E /* kk */, + 4389B08A2C57B27500C52A68 /* ko */, + 43AB384A2C57B33900F19287 /* nl */, + 430439F42C57B34D005518C3 /* nn */, + 43328A002C57B38800444F7E /* pa-IN */, + 438B16442C57B39C007BD1A3 /* pl */, + 439A9F662C57B3AF00448564 /* pt-BR */, + 43A9086F2C57B3C300698450 /* pt-PT */, + 436705DA2C57B3D7002C7FE6 /* rm */, + 431D82792C57B3F600062D71 /* ru */, + 43A4B26E2C57B43700EF1B91 /* sk */, + 43B3869D2C57B446007728B6 /* sl */, + 43C2B2312C57B4560010E539 /* sq */, + 432985492C57B47B00FE7CA2 /* sv */, + 437E67472C57B4AB00A8791A /* th */, + 43B956C12C57B4C800F0E77B /* tr */, + 43D6F80C2C57B4E7004BFBB8 /* ug */, + 43571C312C57B52200A2DD89 /* vi */, + 43BFE5BA2C57B54100CFB4C5 /* zh-CN */, + 43CE9F3D2C57B5500069B3AF /* zh-TW */, + 437511122C60EA6E001E649A /* br */, + 4391631F2C60EA8C00263662 /* ca */, + 4304EDF32C60EABA00F3C9DE /* cy */, + 43E0BE422C60EC4C004FED65 /* hr */, + 433D64C72C60EE1100CEC389 /* nb */, + 43E7071E2C6A26FC00A29AE4 /* hu */, + 43A04D712C73605C0052F8EB /* eu */, + 43FECBDD2C7C9ACA00D644AE /* eo */, + 43C09A7F2C7C9EB400BCD71D /* uk */, + ); + name = Toolbar.strings; + sourceTree = ""; + }; 43F118FE2A52E42400C44C6C /* CustomizeFirefoxHome.strings */ = { isa = PBXVariantGroup; children = ( @@ -22743,7 +23279,7 @@ repositoryURL = "https://github.com/mozilla/rust-components-swift.git"; requirement = { kind = exactVersion; - version = 130.0.20240713050324; + version = 131.0.20240821050323; }; }; 435C85EE2788F4D00072B526 /* XCRemoteSwiftPackageReference "glean-swift" */ = { @@ -22751,7 +23287,7 @@ repositoryURL = "https://github.com/mozilla/glean-swift"; requirement = { kind = exactVersion; - version = 60.3.0; + version = 60.5.0; }; }; 4368F83B279669690013419B /* XCRemoteSwiftPackageReference "SnapKit" */ = { @@ -22783,7 +23319,7 @@ repositoryURL = "https://github.com/getsentry/sentry-cocoa.git"; requirement = { kind = exactVersion; - version = 8.21.0; + version = 8.34.0; }; }; 8AB30EC62B6C038600BD9A9B /* XCRemoteSwiftPackageReference "lottie-ios" */ = { diff --git a/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index f8a94c07ad18..832902767ed7 100644 --- a/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -41,8 +41,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/mozilla/glean-swift", "state" : { - "revision" : "38c00984dcbc7aff85352b917a40f95395044eab", - "version" : "60.3.0" + "revision" : "2185e2eea2d5ce272ff5c0e851eed42d52104ce4", + "version" : "60.5.0" } }, { @@ -95,8 +95,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/mozilla/rust-components-swift.git", "state" : { - "revision" : "08194054c9f54a430a0101d682382150984231bc", - "version" : "130.0.20240713050324" + "revision" : "368989055f07f4e2c135ebed7f4d649b22c1dae2", + "version" : "131.0.20240821050323" } }, { @@ -104,8 +104,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/getsentry/sentry-cocoa.git", "state" : { - "revision" : "38f4f70d07117b9f958a76b1bff278c2f29ffe0e", - "version" : "8.21.0" + "revision" : "d2ced2d961b34573ebd2ea0567a2f1408e90f0ae", + "version" : "8.34.0" } }, { @@ -122,8 +122,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-asn1.git", "state" : { - "revision" : "c7e239b5c1492ffc3ebd7fbcc7a92548ce4e78f0", - "version" : "1.1.0" + "revision" : "df5d2fcd22e3f480e3ef85bf23e277a4a0ef524d", + "version" : "1.2.0" } }, { @@ -140,8 +140,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-crypto.git", "state" : { - "revision" : "f0525da24dc3c6cbb2b6b338b65042bc91cbc4bb", - "version" : "3.3.0" + "revision" : "a53a7e8f858902659d4784322bede34f4e49097e", + "version" : "3.6.1" } }, { diff --git a/firefox-ios/Client/AdjustHelper.swift b/firefox-ios/Client/AdjustHelper.swift index 438e0eb67ec5..3498cc18dd60 100644 --- a/firefox-ios/Client/AdjustHelper.swift +++ b/firefox-ios/Client/AdjustHelper.swift @@ -1,124 +1,126 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/ - -import Common -import Foundation -import Adjust -import Shared - -final class AdjustHelper: NSObject, FeatureFlaggable { - private static let adjustAppTokenKey = "AdjustAppToken" - private let profile: Profile - private let telemetryHelper: AdjustTelemetryProtocol - - init(profile: Profile, - telemetryHelper: AdjustTelemetryProtocol = AdjustTelemetryHelper()) { - self.profile = profile - self.telemetryHelper = telemetryHelper - let sendUsageData = profile.prefs.boolForKey(AppConstants.prefSendUsageData) ?? true - - // This is required for adjust to work properly with ASA and we avoid directly disabling - // third-party sharing as there is a specific method provided to us by adjust for that. - // Note: These settings are persisted on the adjust backend as well - if sendUsageData { - if let adjustThirdPartySharing = ADJThirdPartySharing(isEnabledNumberBool: true) { - Adjust.trackThirdPartySharing(adjustThirdPartySharing) - } - } else { - Adjust.disableThirdPartySharing() - } - } - - func setupAdjust() { - guard let config = getConfig() else { return } - - // Always initialize Adjust if we have a config - otherwise we cannot enable/disable it later. Their SDK must be - // initialized through appDidFinishLaunching otherwise it will be in a bad state. - Adjust.appDidLaunch(config) - - AdjustHelper.setEnabled(shouldEnable) - } - - /// Used to enable or disable Adjust SDK and it's features. - /// If user has disabled Send Anonymous Usage Data then we ask Adjust to erase the user's data as well. - static func setEnabled(_ enabled: Bool) { - Adjust.setEnabled(enabled) - - if !enabled { - Adjust.disableThirdPartySharing() - Adjust.gdprForgetMe() - } - } - +// swiftlint:disable comment_spacing file_header +//// This Source Code Form is subject to the terms of the Mozilla Public +//// License, v. 2.0. If a copy of the MPL was not distributed with this +//// file, You can obtain one at http://mozilla.org/MPL/2.0/ +// +//import Common +//import Foundation +//import Adjust +//import Shared +// +//final class AdjustHelper: NSObject, FeatureFlaggable { +// private static let adjustAppTokenKey = "AdjustAppToken" +// private let profile: Profile +// private let telemetryHelper: AdjustTelemetryProtocol +// +// init(profile: Profile, +// telemetryHelper: AdjustTelemetryProtocol = AdjustTelemetryHelper()) { +// self.profile = profile +// self.telemetryHelper = telemetryHelper +// let sendUsageData = profile.prefs.boolForKey(AppConstants.prefSendUsageData) ?? true +// +// // This is required for adjust to work properly with ASA and we avoid directly disabling +// // third-party sharing as there is a specific method provided to us by adjust for that. +// // Note: These settings are persisted on the adjust backend as well +// if sendUsageData { +// if let adjustThirdPartySharing = ADJThirdPartySharing(isEnabledNumberBool: true) { +// Adjust.trackThirdPartySharing(adjustThirdPartySharing) +// } +// } else { +// Adjust.disableThirdPartySharing() +// } +// } +// +// func setupAdjust() { +// guard let config = getConfig() else { return } +// +// // Always initialize Adjust if we have a config - otherwise we cannot enable/disable it later. Their SDK must be +// // initialized through appDidFinishLaunching otherwise it will be in a bad state. +// Adjust.appDidLaunch(config) +// +// AdjustHelper.setEnabled(shouldEnable) +// } +// +// /// Used to enable or disable Adjust SDK and it's features. +// /// If user has disabled Send Anonymous Usage Data then we ask Adjust to erase the user's data as well. +// static func setEnabled(_ enabled: Bool) { +// Adjust.setEnabled(enabled) +// +// if !enabled { +// Adjust.disableThirdPartySharing() +// Adjust.gdprForgetMe() +// } +// } +// // MARK: - Private - - private func getConfig() -> ADJConfig? { - let bundle = AppInfo.applicationBundle - guard let appToken = bundle.object(forInfoDictionaryKey: AdjustHelper.adjustAppTokenKey) as? String, - !appToken.isEmpty else { - return nil - } - - let isProd = featureFlags.isCoreFeatureEnabled(.adjustEnvironmentProd) - let environment = isProd ? ADJEnvironmentProduction : ADJEnvironmentSandbox - let config = ADJConfig(appToken: appToken, environment: environment) - config?.logLevel = isProd ? ADJLogLevelSuppress : ADJLogLevelDebug - - // Record attribution changes - // https://help.adjust.com/en/article/ios-sdk-adjconfig-class#set-up-delegate - config?.delegate = (self as AdjustHelper) - - return config - } - - /// Return true if Adjust should be enabled. If the user has disabled the Send Anonymous Usage Data - /// then we only do one ping to get the attribution and turn it off (i.e. we only enable it if we - /// have not seen the attribution data yet). - private var shouldEnable: Bool { - return shouldTrackRetention || !hasAttribution - } - - /// Return true if retention (session) tracking should be enabled. - /// This follows the Send Anonymous Usage Data setting. - private var shouldTrackRetention: Bool { - return profile.prefs.boolForKey(AppConstants.prefSendUsageData) ?? true - } - +// +// private func getConfig() -> ADJConfig? { +// let bundle = AppInfo.applicationBundle +// guard let appToken = bundle.object(forInfoDictionaryKey: AdjustHelper.adjustAppTokenKey) as? String, +// !appToken.isEmpty else { +// return nil +// } +// +// let isProd = featureFlags.isCoreFeatureEnabled(.adjustEnvironmentProd) +// let environment = isProd ? ADJEnvironmentProduction : ADJEnvironmentSandbox +// let config = ADJConfig(appToken: appToken, environment: environment) +// config?.logLevel = isProd ? ADJLogLevelSuppress : ADJLogLevelDebug +// +// // Record attribution changes +// // https://help.adjust.com/en/article/ios-sdk-adjconfig-class#set-up-delegate +// config?.delegate = (self as AdjustHelper) +// +// return config +// } +// +// /// Return true if Adjust should be enabled. If the user has disabled the Send Anonymous Usage Data +// /// then we only do one ping to get the attribution and turn it off (i.e. we only enable it if we +// /// have not seen the attribution data yet). +// private var shouldEnable: Bool { +// return shouldTrackRetention || !hasAttribution +// } +// +// /// Return true if retention (session) tracking should be enabled. +// /// This follows the Send Anonymous Usage Data setting. +// private var shouldTrackRetention: Bool { +// return profile.prefs.boolForKey(AppConstants.prefSendUsageData) ?? true +// } +// // MARK: - UserDefaults - - private enum UserDefaultsKey: String { - case hasAttribution = "com.moz.adjust.hasAttribution.key" - } - - private var hasAttribution: Bool { - get { UserDefaults.standard.object(forKey: UserDefaultsKey.hasAttribution.rawValue) as? Bool ?? false } - set { UserDefaults.standard.set(newValue, forKey: UserDefaultsKey.hasAttribution.rawValue) } - } -} - +// +// private enum UserDefaultsKey: String { +// case hasAttribution = "com.moz.adjust.hasAttribution.key" +// } +// +// private var hasAttribution: Bool { +// get { UserDefaults.standard.object(forKey: UserDefaultsKey.hasAttribution.rawValue) as? Bool ?? false } +// set { UserDefaults.standard.set(newValue, forKey: UserDefaultsKey.hasAttribution.rawValue) } +// } +//} +// // MARK: - AdjustDelegate -extension AdjustHelper: AdjustDelegate { - /// This is called when Adjust has figured out the attribution. It will call us with a summary - /// of all the things it knows. Like the campaign ID. We simply save a boolean that attribution - /// has changed so we know the single attribution ping to Adjust was done. - /// - /// We also disable Adjust based on the Send Anonymous Usage Data setting. - func adjustAttributionChanged(_ attribution: ADJAttribution?) { - hasAttribution = true - if !shouldEnable { - AdjustHelper.setEnabled(false) - } - - telemetryHelper.setAttributionData(attribution) - } - - func adjustDeeplinkResponse(_ deeplink: URL?) -> Bool { - guard let url = deeplink else { return true } - - // Send telemetry if url is not nil - let attribution = Adjust.attribution() - telemetryHelper.sendDeeplinkTelemetry(url: url, attribution: attribution) - return true - } -} +//extension AdjustHelper: AdjustDelegate { +// /// This is called when Adjust has figured out the attribution. It will call us with a summary +// /// of all the things it knows. Like the campaign ID. We simply save a boolean that attribution +// /// has changed so we know the single attribution ping to Adjust was done. +// /// +// /// We also disable Adjust based on the Send Anonymous Usage Data setting. +// func adjustAttributionChanged(_ attribution: ADJAttribution?) { +// hasAttribution = true +// if !shouldEnable { +// AdjustHelper.setEnabled(false) +// } +// +// telemetryHelper.setAttributionData(attribution) +// } +// +// func adjustDeeplinkResponse(_ deeplink: URL?) -> Bool { +// guard let url = deeplink else { return true } +// +// // Send telemetry if url is not nil +// let attribution = Adjust.attribution() +// telemetryHelper.sendDeeplinkTelemetry(url: url, attribution: attribution) +// return true +// } +//} +// swiftlint:enable comment_spacing file_header diff --git a/firefox-ios/Client/AdjustTelemetryHelper.swift b/firefox-ios/Client/AdjustTelemetryHelper.swift index 857b9e19f367..0db7a0578f83 100644 --- a/firefox-ios/Client/AdjustTelemetryHelper.swift +++ b/firefox-ios/Client/AdjustTelemetryHelper.swift @@ -1,58 +1,60 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/ - -import Foundation -import Adjust - -protocol AdjustTelemetryData { - var campaign: String? { get set } - var adgroup: String? { get set } - var creative: String? { get set } - var network: String? { get set } -} - -extension ADJAttribution: AdjustTelemetryData { -} - -protocol AdjustTelemetryProtocol { - func setAttributionData(_ attribution: AdjustTelemetryData?) - func sendDeeplinkTelemetry(url: URL, attribution: AdjustTelemetryData?) -} - -class AdjustTelemetryHelper: AdjustTelemetryProtocol { - var gleanWrapper: GleanWrapper - var telemetry: AdjustWrapper - - init(gleanWrapper: GleanWrapper = DefaultGleanWrapper(), - telemetry: AdjustWrapper = DefaultAdjustWrapper()) { - self.gleanWrapper = gleanWrapper - self.telemetry = telemetry - } - - func sendDeeplinkTelemetry(url: URL, attribution: AdjustTelemetryData?) { - telemetry.recordDeeplink(url: url) - - setAttributionData(attribution) - } - - func setAttributionData(_ attribution: AdjustTelemetryData?) { - if let campaign = attribution?.campaign { - telemetry.record(campaign: campaign) - } - - if let adgroup = attribution?.adgroup { - telemetry.record(adgroup: adgroup) - } - - if let creative = attribution?.creative { - telemetry.record(creative: creative) - } - - if let network = attribution?.network { - telemetry.record(network: network) - } - - gleanWrapper.submitPing() - } -} +// swiftlint:disable comment_spacing file_header +//// This Source Code Form is subject to the terms of the Mozilla Public +//// License, v. 2.0. If a copy of the MPL was not distributed with this +//// file, You can obtain one at http://mozilla.org/MPL/2.0/ +// +//import Foundation +//import Adjust +// +//protocol AdjustTelemetryData { +// var campaign: String? { get set } +// var adgroup: String? { get set } +// var creative: String? { get set } +// var network: String? { get set } +//} +// +//extension ADJAttribution: AdjustTelemetryData { +//} +// +//protocol AdjustTelemetryProtocol { +// func setAttributionData(_ attribution: AdjustTelemetryData?) +// func sendDeeplinkTelemetry(url: URL, attribution: AdjustTelemetryData?) +//} +// +//class AdjustTelemetryHelper: AdjustTelemetryProtocol { +// var gleanWrapper: GleanWrapper +// var telemetry: AdjustWrapper +// +// init(gleanWrapper: GleanWrapper = DefaultGleanWrapper(), +// telemetry: AdjustWrapper = DefaultAdjustWrapper()) { +// self.gleanWrapper = gleanWrapper +// self.telemetry = telemetry +// } +// +// func sendDeeplinkTelemetry(url: URL, attribution: AdjustTelemetryData?) { +// telemetry.recordDeeplink(url: url) +// +// setAttributionData(attribution) +// } +// +// func setAttributionData(_ attribution: AdjustTelemetryData?) { +// if let campaign = attribution?.campaign { +// telemetry.record(campaign: campaign) +// } +// +// if let adgroup = attribution?.adgroup { +// telemetry.record(adgroup: adgroup) +// } +// +// if let creative = attribution?.creative { +// telemetry.record(creative: creative) +// } +// +// if let network = attribution?.network { +// telemetry.record(network: network) +// } +// +// gleanWrapper.submitPing() +// } +//} +// swiftlint:enable comment_spacing file_header diff --git a/firefox-ios/Client/Application/AccessibilityIdentifiers.swift b/firefox-ios/Client/Application/AccessibilityIdentifiers.swift index e9c50cea1685..0ee388e13aad 100644 --- a/firefox-ios/Client/Application/AccessibilityIdentifiers.swift +++ b/firefox-ios/Client/Application/AccessibilityIdentifiers.swift @@ -16,6 +16,7 @@ public struct AccessibilityIdentifiers { /// Used for toolbar/URL bar buttons since our classes are built that buttons can live in one or the other /// Using only those a11y identifiers for both ensures we have standard way to refer to buttons from iPad to iPhone struct Toolbar { + static let urlBarBorder = "TabToolbar.urlBarBorder" static let settingsMenuButton = "TabToolbar.menuButton" static let homeButton = "TabToolbar.homeButton" static let trackingProtection = "TabLocationView.trackingProtectionButton" @@ -71,6 +72,47 @@ public struct AccessibilityIdentifiers { static let actionButton = "ContextualHints.ActionButton" } + struct EnhancedTrackingProtection { + struct MainScreen { + static let clearCookiesButton = "TrackingProtection.ClearCookiesButton" + static let trackingProtectionSettingsButton = "TrackingProtection.SettingsButton" + static let foxImage = "TrackingProtection.FoxStatusImage" + static let shieldImage = "TrackingProtection.ShieldImage" + static let lockImage = "TrackingProtection.LockImage" + static let arrowImage = "TrackingProtection.ArrowImage" + static let domainLabel = "TrackingProtection.DomainTitleLabel" + static let domainHeaderLabel = "TrackingProtection.DomainHeaderLabel" + static let statusTitleLabel = "TrackingProtection.ConnectionStatusTitleLabel" + static let statusBodyLabel = "TrackingProtection.ConnectionStatusBodyLabel" + static let trackersBlockedLabel = "TrackingProtection.TrackersBlockedLabel" + static let securityStatusLabel = "TrackingProtection.ConnectionSecurityStatusLabel" + static let toggleViewTitleLabel = "TrackingProtection.ToggleViewTitleLabel" + static let toggleViewBodyLabel = "TrackingProtection.ToggleViewBodyLabel" + static let closeButton = "TrackingProtection.CloseButton" + static let faviconImage = "TrackingProtection.FaviconImage" + } + + struct DetailsScreen { + static let headerView = "TrackingProtectionDetails.HeaderView" + static let mainView = "TrackingProtectionDetails.MainView" + static let containerView = "TrackingProtectionDetails.BaseView" + static let connectionView = "TrackingProtectionDetails.ConnectionView" + } + + struct BlockedTrackers { + static let headerView = "BlockedTrackers.HeaderView" + static let mainView = "BlockedTrackers.MainView" + static let containerView = "BlockedTrackers.BaseView" + static let crossSiteTrackersView = "BlockedTrackers.CrossSiteTrackersView" + static let socialMediaTrackersView = "BlockedTrackers.SocialMediaTrackersView" + static let fingerprintersView = "BlockedTrackers.FingerprintersView" + static let crossSiteTrackersImage = "BlockedTrackers.CrossSiteTrackersImage" + static let socialMediaTrackersImage = "BlockedTrackers.SocialMediaTrackersImage" + static let fingerprintersImage = "BlockedTrackers.FingerprintersImage" + static let blockedTrackersLabel = "BlockedTrackers.BlockedTrackersLabel" + } + } + struct FirefoxHomepage { static let collectionView = "FxCollectionView" @@ -468,6 +510,7 @@ public struct AccessibilityIdentifiers { static let fxaSignInButton = "EmailSignIn.button" static let qrButton = "QRCodeSignIn.button" static let qrScanFailedAlertOkButton = "qrCodeAlert.okButton" + static let signInButton = "Sign in" } struct Search { @@ -573,6 +616,10 @@ public struct AccessibilityIdentifiers { static let title = "showLinkPreviews" } + struct ClosePrivateTabs { + static let title = "ClosePrivateTabs" + } + struct SearchBar { static let searchBarSetting = "SearchBarSetting" static let topSetting = "TopSearchBar" diff --git a/firefox-ios/Client/Application/AppDelegate+SyncSentTabs.swift b/firefox-ios/Client/Application/AppDelegate+SyncSentTabs.swift index a0956a4e3698..ad555e43fecf 100644 --- a/firefox-ios/Client/Application/AppDelegate+SyncSentTabs.swift +++ b/firefox-ios/Client/Application/AppDelegate+SyncSentTabs.swift @@ -10,16 +10,17 @@ import Account import Common extension UIApplication { - var sendTabDelegate: SendTabDelegate { - return AppSendTabDelegate(app: self) + var fxaCommandsDelegate: FxACommandsDelegate { + return AppFxACommandsDelegate(app: self) } } -/// Sent tabs can be displayed not only by receiving push notifications, but by sync. +/// Close and Sent tabs can be displayed not only by receiving push notifications, +/// but by sync. /// Sync will get the list of sent tabs, and try to display any in that list. -/// Thus, push notifications are not needed to receive sent tabs, they can be handled -/// when the app performs a sync. -class AppSendTabDelegate: SendTabDelegate { +/// Thus, push notifications are not needed to receive sent or closed tabs; +/// they can be handled when the app performs a sync. +class AppFxACommandsDelegate: FxACommandsDelegate { private let app: ApplicationStateProvider private let logger: Logger private var applicationHelper: ApplicationHelper @@ -46,4 +47,10 @@ class AppSendTabDelegate: SendTabDelegate { } } } + + func closeTabs(for urls: [URL]) { + Task { + await self.applicationHelper.closeTabs(urls) + } + } } diff --git a/firefox-ios/Client/Application/AppDelegate.swift b/firefox-ios/Client/Application/AppDelegate.swift index c75da4485cb6..f0645d45d105 100644 --- a/firefox-ios/Client/Application/AppDelegate.swift +++ b/firefox-ios/Client/Application/AppDelegate.swift @@ -25,7 +25,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { lazy var profile: Profile = BrowserProfile( localName: "profile", - sendTabDelegate: UIApplication.shared.sendTabDelegate, + fxaCommandsDelegate: UIApplication.shared.fxaCommandsDelegate, creditCardAutofillEnabled: creditCardAutofillStatus ) diff --git a/firefox-ios/Client/Application/AppLaunchUtil.swift b/firefox-ios/Client/Application/AppLaunchUtil.swift index 47b2667e2426..fefd5d5df2a4 100644 --- a/firefox-ios/Client/Application/AppLaunchUtil.swift +++ b/firefox-ios/Client/Application/AppLaunchUtil.swift @@ -11,7 +11,7 @@ import Glean class AppLaunchUtil { private var logger: Logger - private var adjustHelper: AdjustHelper +// private var adjustHelper: AdjustHelper private var profile: Profile private let introScreenManager: IntroScreenManager @@ -19,7 +19,7 @@ class AppLaunchUtil { profile: Profile) { self.logger = logger self.profile = profile - self.adjustHelper = AdjustHelper(profile: profile) +// self.adjustHelper = AdjustHelper(profile: profile) self.introScreenManager = IntroScreenManager(prefs: profile.prefs) } @@ -78,7 +78,13 @@ class AppLaunchUtil { } } - RustFirefoxAccounts.startup(prefs: profile.prefs) { _ in + // RustFirefoxAccounts doesn't have access to the feature flags + // So we check the nimbus flag here before sending it to the startup + var fxaFeatures: RustFxAFeatures = [] + if LegacyFeatureFlagsManager.shared.isFeatureEnabled(.closeRemoteTabs, checking: .buildAndUser) { + fxaFeatures.insert(.closeRemoteTabs) + } + RustFirefoxAccounts.startup(prefs: profile.prefs, features: fxaFeatures) { _ in self.logger.log("RustFirefoxAccounts started", level: .info, category: .sync) AppEventQueue.signal(event: .accountManagerInitialized) } @@ -122,7 +128,7 @@ class AppLaunchUtil { } updateSessionCount() - adjustHelper.setupAdjust() +// adjustHelper.setupAdjust() AppEventQueue.signal(event: .postLaunchDependenciesComplete) } diff --git a/firefox-ios/Client/Application/ImageIdentifiers.swift b/firefox-ios/Client/Application/ImageIdentifiers.swift index 47c6e66d704d..cc04ab6b4486 100644 --- a/firefox-ios/Client/Application/ImageIdentifiers.swift +++ b/firefox-ios/Client/Application/ImageIdentifiers.swift @@ -61,4 +61,14 @@ public struct ImageIdentifiers { public static let sync = "onboardingSyncCTD" } } + + public struct TrackingProtection { + public static let protectionAlert = "protectionAlert" + public static let protectionOn = "protectionOn" + public static let protectionOff = "protectionOff" + + public static let socialMediaTrackers = "socialMediaTrackersImage" + public static let fingerprintersTrackers = "fingerprintersTrackersImage" + public static let crossSiteTrackers = "crossSiteTrackersImage" + } } diff --git a/firefox-ios/Client/Application/SceneDelegate.swift b/firefox-ios/Client/Application/SceneDelegate.swift index c652e9b115fa..62932a62a3f9 100644 --- a/firefox-ios/Client/Application/SceneDelegate.swift +++ b/firefox-ios/Client/Application/SceneDelegate.swift @@ -35,13 +35,19 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { options connectionOptions: UIScene.ConnectionOptions ) { guard !AppConstants.isRunningUnitTest else { return } + logger.log("SceneDelegate: will connect to session", level: .info, category: .lifecycle) // Add hooks for the nimbus-cli to test experiments on device or involving deeplinks. if let url = connectionOptions.urlContexts.first?.url { Experiments.shared.initializeTooling(url: url) } - routeBuilder.configure(prefs: profile.prefs) + routeBuilder.configure( + isPrivate: UserDefaults.standard.bool( + forKey: PrefsKeys.LastSessionWasPrivate + ), + prefs: profile.prefs + ) let sceneCoordinator = SceneCoordinator(scene: scene) self.sceneCoordinator = sceneCoordinator @@ -51,6 +57,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } func sceneDidDisconnect(_ scene: UIScene) { + let logUUID = sceneCoordinator?.windowUUID.uuidString ?? "" + logger.log("SceneDelegate: scene did disconnect. UUID: \(logUUID)", level: .info, category: .lifecycle) // Handle clean-up here for closing windows on iPad guard let sceneCoordinator = (scene.delegate as? SceneDelegate)?.sceneCoordinator else { return } @@ -73,6 +81,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { /// or other activities that need to begin. func sceneDidBecomeActive(_ scene: UIScene) { guard !AppConstants.isRunningUnitTest else { return } + let logUUID = sceneCoordinator?.windowUUID.uuidString ?? "" + logger.log("SceneDelegate: scene did become active. UUID: \(logUUID)", level: .info, category: .lifecycle) // Resume previously stopped downloads for, and on, THIS scene only. if let uuid = sceneCoordinator?.windowUUID { @@ -87,6 +97,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { /// Use this method to reduce the scene's memory usage, clear claims to resources & dependencies / services. /// UIKit takes a snapshot of the scene for the app switcher after this method returns. func sceneDidEnterBackground(_ scene: UIScene) { + let logUUID = sceneCoordinator?.windowUUID.uuidString ?? "" + logger.log("SceneDelegate: scene did enter background. UUID: \(logUUID)", level: .info, category: .lifecycle) if let uuid = sceneCoordinator?.windowUUID { downloadQueue.pauseAll(for: uuid) } @@ -133,7 +145,12 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } func handleOpenURL(_ url: URL) { - routeBuilder.configure(prefs: profile.prefs) + routeBuilder.configure( + isPrivate: UserDefaults.standard.bool( + forKey: PrefsKeys.LastSessionWasPrivate + ), + prefs: profile.prefs + ) // Before processing the incoming URL, check if it is a widget that is opening a tab via UUID. // If so, we want to be sure that we select the tab in the correct iPad window. diff --git a/firefox-ios/Client/Application/UITestAppDelegate.swift b/firefox-ios/Client/Application/UITestAppDelegate.swift index 7f347a7daed4..7f9d71402c57 100644 --- a/firefox-ios/Client/Application/UITestAppDelegate.swift +++ b/firefox-ios/Client/Application/UITestAppDelegate.swift @@ -34,12 +34,7 @@ class UITestAppDelegate: AppDelegate, FeatureFlaggable { launchArguments.forEach { arg in if arg.starts(with: LaunchArguments.ServerPort) { - let portString = arg.replacingOccurrences(of: LaunchArguments.ServerPort, with: "") - if let port = Int(portString) { - AppInfo.webserverPort = port - } else { - fatalError("Failed to set web server port override.") - } + configureWebserverPort(arg) } if arg.starts(with: LaunchArguments.LoadDatabasePrefix) { @@ -134,13 +129,13 @@ class UITestAppDelegate: AppDelegate, FeatureFlaggable { // Use a clean profile for each test session. profile = BrowserProfile( localName: "testProfile", - sendTabDelegate: application.sendTabDelegate, + fxaCommandsDelegate: application.fxaCommandsDelegate, clear: true ) } else { profile = BrowserProfile( localName: "testProfile", - sendTabDelegate: application.sendTabDelegate + fxaCommandsDelegate: application.fxaCommandsDelegate ) } @@ -200,6 +195,15 @@ class UITestAppDelegate: AppDelegate, FeatureFlaggable { return profile } + private func configureWebserverPort(_ arg: String) { + let portString = arg.replacingOccurrences(of: LaunchArguments.ServerPort, with: "") + if let port = Int(portString) { + AppInfo.webserverPort = port + } else { + fatalError("Failed to set web server port override.") + } + } + override func application( _ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil diff --git a/firefox-ios/Client/Application/WindowManager.swift b/firefox-ios/Client/Application/WindowManager.swift index 69a7a3c8d32a..3ed5d022b560 100644 --- a/firefox-ios/Client/Application/WindowManager.swift +++ b/firefox-ios/Client/Application/WindowManager.swift @@ -24,10 +24,6 @@ enum MultiWindowAction { /// General window management class that provides some basic coordination and /// state management for multiple windows shared across a single running app. protocol WindowManager { - /// The UUID of the active window (there is always at least 1, except in - /// the earliest stages of app startup lifecycle) - var activeWindow: WindowUUID { get set } - /// A collection of all open windows and their related metadata. var windows: [WindowUUID: AppWindowInfo] { get } @@ -60,7 +56,7 @@ protocol WindowManager { /// windows are being restored concurrently, we never supply the same UUID /// to more than one window. /// - Returns: a UUID for the next window to be opened. - func reserveNextAvailableWindowUUID() -> WindowUUID + func reserveNextAvailableWindowUUID() -> ReservedWindowUUID /// Signals the WindowManager that a window event has occurred. Window events /// are communicated to any interested Coordinators for _all_ windows, but @@ -91,15 +87,16 @@ final class WindowManagerImplementation: WindowManager, WindowTabsSyncCoordinato } private(set) var windows: [WindowUUID: AppWindowInfo] = [:] + + // A list of UUIDs that have already been reserved for windows which are being actively configured. + // Because so much of our early launch configuration occurs async, it's possible to request more than + // one window UUID before previous windows have completed, this tracks reserved UUIDs still in the + // process of initial configuration. private var reservedUUIDs: [WindowUUID] = [] - var activeWindow: WindowUUID { - get { return uuidForActiveWindow() } - set { _activeWindowUUID = newValue } - } + private let logger: Logger private let tabDataStore: TabDataStore private let defaults: UserDefaultsInterface - private var _activeWindowUUID: WindowUUID? private let tabSyncCoordinator = WindowTabsSyncCoordinator() private let widgetSimpleTabsCoordinator = WindowSimpleTabsCoordinator() @@ -132,16 +129,14 @@ final class WindowManagerImplementation: WindowManager, WindowTabsSyncCoordinato func newBrowserWindowConfigured(_ windowInfo: AppWindowInfo, uuid: WindowUUID) { updateWindow(windowInfo, for: uuid) - if let reservedUUIDIdx = reservedUUIDs.firstIndex(where: { $0 == uuid }) { - reservedUUIDs.remove(at: reservedUUIDIdx) - } + clearReservedUUID(uuid) } func tabManager(for windowUUID: WindowUUID) -> TabManager { guard let tabManager = window(for: windowUUID)?.tabManager else { assertionFailure("Tab Manager unavailable for requested UUID: \(windowUUID). This is a client error.") logger.log("No tab manager for window UUID.", level: .fatal, category: .window) - return window(for: activeWindow)?.tabManager ?? windows.first!.value.tabManager! + return windows.first!.value.tabManager! } return tabManager @@ -158,6 +153,8 @@ final class WindowManagerImplementation: WindowManager, WindowTabsSyncCoordinato func windowWillClose(uuid: WindowUUID) { postWindowEvent(event: .windowWillClose, windowUUID: uuid) updateWindow(nil, for: uuid) + // Fix edge case in which a scene's UUID might still be reserved when the scene is disconnected + clearReservedUUID(uuid) // Closed windows are popped off and moved behind any already-open windows in the list var prefs = windowOrderingPriority @@ -168,9 +165,12 @@ final class WindowManagerImplementation: WindowManager, WindowTabsSyncCoordinato windowOrderingPriority = prefs } - func reserveNextAvailableWindowUUID() -> WindowUUID { + func reserveNextAvailableWindowUUID() -> ReservedWindowUUID { // Continue to provide the expected hardcoded UUID for UI tests. - guard !AppConstants.isRunningUITests else { return WindowUUID.DefaultUITestingUUID } + guard !AppConstants.isRunningUITests else { + return ReservedWindowUUID(uuid: WindowUUID.DefaultUITestingUUID, isNew: false) + } + assert(Thread.isMainThread, "Window UUID configuration currently expected on main thread only.") // • If no saved windows (tab data), we generate a new UUID. // • If user has saved windows (tab data), we return the first available UUID @@ -184,11 +184,53 @@ final class WindowManagerImplementation: WindowManager, WindowTabsSyncCoordinato // Fetch available window data on disk, and remove any already-opened windows // or UUIDs that are already reserved and in the process of opening. let openWindowUUIDs = windows.keys - let filteredUUIDs = tabDataStore.fetchWindowDataUUIDs().filter { - !openWindowUUIDs.contains($0) && !reservedUUIDs.contains($0) + let onDiskUUIDs = tabDataStore.fetchWindowDataUUIDs() + + let onDiskUUIDLog = onDiskUUIDs.map({ $0.uuidString.prefix(8) }).joined(separator: ", ") + let reserveLog = reservedUUIDs.map({ $0.uuidString.prefix(8) }).joined(separator: ", ") + let openLog = openWindowUUIDs.map({ $0.uuidString.prefix(8) }).joined(separator: ", ") + logger.log("WindowManager: reserve next UUID. Disk: \(onDiskUUIDLog). Reserved: \(reserveLog). Open: \(openLog)", + level: .debug, + category: .window) + + // On iPhone devices, we expect there only to ever be a single window. If there + // are >1 windows we've encountered some type of unexpected state. + let isIpad = (UIDevice.current.userInterfaceIdiom == .pad) + + let result: ReservedWindowUUID + // TODO: [9610] Unit tests should eventually be updated for these changes. Forthcoming. + if !isIpad && !AppConstants.isRunningUnitTest { + // We should always have either a single UUID on disk or no UUIDs because this is a brand new app install + if onDiskUUIDs.isEmpty { + result = ReservedWindowUUID(uuid: WindowUUID(), isNew: true) + } else { + result = ReservedWindowUUID(uuid: onDiskUUIDs.first!, isNew: false) + + let uuidCount = onDiskUUIDs.count + if uuidCount > 1 { + // This is unexpected. Potentially related to incident in v128 (see: FXIOS-9516). + // On iPhone, we should never have more than 1 window tab file. Log an error and + // clean up the UUID(s) that we know won't be used. We expect a certain number of + // these fatal errors to be logged for users previously impacted by the above, and + // then it should fall to zero. + logger.log("Detected multiple window tab files on iPhone (UUID count: \(uuidCount))", + level: .fatal, + category: .window) + let uuidsToDelete = Array(onDiskUUIDs.dropFirst()) + Task { await tabDataStore.removeWindowData(forUUIDs: uuidsToDelete) } + } + } + } else { + let filteredUUIDs = onDiskUUIDs.filter { + !openWindowUUIDs.contains($0) && !reservedUUIDs.contains($0) + } + + result = nextWindowUUIDToOpen(filteredUUIDs) } - let result = nextWindowUUIDToOpen(filteredUUIDs) + logger.log("WindowManager: reserve next UUID result = \(result.uuid.uuidString) Is new?: \(result.isNew)", + level: .debug, + category: .window) let resultUUID = result.uuid if result.isNew { // Be sure to add any brand-new windows to our ordering preferences @@ -199,7 +241,7 @@ final class WindowManagerImplementation: WindowManager, WindowTabsSyncCoordinato // Reserve the UUID until the Client finishes the window configuration process reservedUUIDs.append(resultUUID) - return resultUUID + return result } func postWindowEvent(event: WindowEvent, windowUUID: WindowUUID) { @@ -245,7 +287,12 @@ final class WindowManagerImplementation: WindowManager, WindowTabsSyncCoordinato // MARK: - Internal Utilities - func saveSimpleTabs() { + private func clearReservedUUID(_ uuid: WindowUUID) { + guard let reservedUUIDIdx = reservedUUIDs.firstIndex(where: { $0 == uuid }) else { return } + reservedUUIDs.remove(at: reservedUUIDIdx) + } + + private func saveSimpleTabs() { let providers = allWindowTabManagers() as? [WindowSimpleTabsProvider] ?? [] widgetSimpleTabsCoordinator.saveSimpleTabs(for: providers) } @@ -257,17 +304,17 @@ final class WindowManagerImplementation: WindowManager, WindowTabsSyncCoordinato /// already open or reserved (this is important - these UUIDs should be pre- /// filtered). /// - Returns: the UUID for the next window that will be opened on iPad. - private func nextWindowUUIDToOpen(_ onDiskUUIDs: [WindowUUID]) -> (uuid: WindowUUID, isNew: Bool) { - func nextUUIDUsingFallbackSorting() -> (uuid: WindowUUID, isNew: Bool) { + private func nextWindowUUIDToOpen(_ onDiskUUIDs: [WindowUUID]) -> ReservedWindowUUID { + func nextUUIDUsingFallbackSorting() -> ReservedWindowUUID { let sortedUUIDs = onDiskUUIDs.sorted(by: { return $0.uuidString > $1.uuidString }) if let resultUUID = sortedUUIDs.first { - return (uuid: resultUUID, isNew: false) + return ReservedWindowUUID(uuid: resultUUID, isNew: false) } - return (uuid: WindowUUID(), isNew: true) + return ReservedWindowUUID(uuid: WindowUUID(), isNew: true) } guard !onDiskUUIDs.isEmpty else { - return (uuid: WindowUUID(), isNew: true) + return ReservedWindowUUID(uuid: WindowUUID(), isNew: true) } // Get the ordering preference @@ -281,7 +328,7 @@ final class WindowManagerImplementation: WindowManager, WindowTabsSyncCoordinato // Iterate and return the first UUID that is available within our on-disk UUIDs // (which excludes windows already open or reserved). for uuid in priorityPreference where onDiskUUIDs.contains(uuid) { - return (uuid: uuid, isNew: false) + return ReservedWindowUUID(uuid: uuid, isNew: false) } return nextUUIDUsingFallbackSorting() @@ -289,38 +336,6 @@ final class WindowManagerImplementation: WindowManager, WindowTabsSyncCoordinato private func updateWindow(_ info: AppWindowInfo?, for uuid: WindowUUID) { windows[uuid] = info - didUpdateWindow(uuid) - } - - /// Called internally when a window is updated (or removed). - /// - Parameter uuid: the UUID of the window that changed. - private func didUpdateWindow(_ uuid: WindowUUID) { - // Convenience. If the client has successfully configured - // a window and it is the only window in the app, we can - // be sure we automatically set it as the active window. - if windows.count == 1 { - activeWindow = windows.keys.first! - } - } - - private func uuidForActiveWindow() -> WindowUUID { - guard !windows.isEmpty else { - // No app windows. Unsupported state; can't recover gracefully. - fatalError() - } - - guard windows.count > 1 else { - // For apps with only 1 window we can always safely return it as the active window. - return windows.keys.first! - } - - guard let uuid = _activeWindowUUID else { - let message = "App has multiple windows but no active window UUID. This is a client error." - logger.log(message, level: .fatal, category: .window) - assertionFailure(message) - return windows.keys.first! - } - return uuid } private func window(for windowUUID: WindowUUID, createIfNeeded: Bool = false) -> AppWindowInfo? { diff --git a/firefox-ios/Client/ApplicationHelper.swift b/firefox-ios/Client/ApplicationHelper.swift index 55925612388f..1079f5b3bce5 100644 --- a/firefox-ios/Client/ApplicationHelper.swift +++ b/firefox-ios/Client/ApplicationHelper.swift @@ -9,6 +9,7 @@ protocol ApplicationHelper { func openSettings() func open(_ url: URL) func open(_ url: URL, inWindow: WindowUUID) + func closeTabs(_ urls: [URL]) async } /// UIApplication.shared wrapper @@ -46,4 +47,17 @@ struct DefaultApplicationHelper: ApplicationHelper { open(url) } } + + /// Closes all tabs that match the url passed in + /// This is most likely from other clients connected to the same + /// account requesting to close the tab on this device + /// + /// - Parameters: + /// - urls: an array of URLs requested to be closed + func closeTabs(_ urls: [URL]) async { + let windowManager = AppContainer.shared.resolve() as WindowManager + for tabManager in windowManager.allWindowTabManagers() { + await tabManager.removeTabs(by: urls) + } + } } diff --git a/firefox-ios/Client/Assets/CC_Script/AutofillTelemetry.sys.mjs b/firefox-ios/Client/Assets/CC_Script/AutofillTelemetry.sys.mjs index a637c3766886..f76afd96581d 100644 --- a/firefox-ios/Client/Assets/CC_Script/AutofillTelemetry.sys.mjs +++ b/firefox-ios/Client/Assets/CC_Script/AutofillTelemetry.sys.mjs @@ -122,7 +122,7 @@ class AutofillTelemetryBase { const extra = this.#initFormEventExtra("unavailable"); for (const fieldDetail of fieldDetails) { - let { filledState, value } = data[fieldDetail.elementId]; + let { filledState, value } = data.get(fieldDetail.elementId); switch (filledState) { case FIELD_STATES.AUTO_FILLED: filledState = "filled"; @@ -152,7 +152,7 @@ class AutofillTelemetryBase { const extra = this.#initFormEventExtra("unavailable"); for (const fieldDetail of fieldDetails) { - let { filledState, value } = data[fieldDetail.elementId]; + let { filledState, value } = data.get(fieldDetail.elementId); switch (filledState) { case FIELD_STATES.AUTO_FILLED: filledState = "autofilled"; diff --git a/firefox-ios/Client/Assets/CC_Script/FormAutofillHandler.sys.mjs b/firefox-ios/Client/Assets/CC_Script/FormAutofillHandler.sys.mjs index f8d7d6309beb..3c0fdb68a14e 100644 --- a/firefox-ios/Client/Assets/CC_Script/FormAutofillHandler.sys.mjs +++ b/firefox-ios/Client/Assets/CC_Script/FormAutofillHandler.sys.mjs @@ -227,7 +227,7 @@ export class FormAutofillHandler { * @param {object} profile * A profile to be previewed with */ - async previewFields(elementIds, profile) { + previewFields(elementIds, profile) { this.getAdaptedProfiles([profile]); for (const fieldDetail of this.fieldDetails) { @@ -945,15 +945,15 @@ export class FormAutofillHandler { * value: The value of the element */ collectFormFilledData() { - const filledData = {}; + const filledData = new Map(); for (const fieldDetail of this.fieldDetails) { const element = fieldDetail.element; - filledData[fieldDetail.elementId] = { + filledData.set(fieldDetail.elementId, { filledState: element.autofillState, filledValue: this.computeFillingValue(fieldDetail), value: element.value, - }; + }); } return filledData; } diff --git a/firefox-ios/Client/Assets/CC_Script/FormAutofillSection.sys.mjs b/firefox-ios/Client/Assets/CC_Script/FormAutofillSection.sys.mjs index f1626ecec1e6..61bb1a52195f 100644 --- a/firefox-ios/Client/Assets/CC_Script/FormAutofillSection.sys.mjs +++ b/firefox-ios/Client/Assets/CC_Script/FormAutofillSection.sys.mjs @@ -294,7 +294,7 @@ export class FormAutofillSection { }; for (const detail of this.fieldDetails) { - const { filledValue } = formFilledData[detail.elementId]; + const { filledValue } = formFilledData.get(detail.elementId); if ( !filledValue || diff --git a/firefox-ios/Client/Assets/CC_Script/FormLikeFactory.sys.mjs b/firefox-ios/Client/Assets/CC_Script/FormLikeFactory.sys.mjs index 4950ee0f8207..162fedbc2bc8 100644 --- a/firefox-ios/Client/Assets/CC_Script/FormLikeFactory.sys.mjs +++ b/firefox-ios/Client/Assets/CC_Script/FormLikeFactory.sys.mjs @@ -37,42 +37,28 @@ export let FormLikeFactory = { }, /** - * Create a FormLike object from an // not in a
are one LoginForm but this + * shouldn't be relied upon as the heuristics may change to detect multiple + * "forms" (e.g. registration and login) on one page with a . * - * @param {HTMLInputElement|HTMLSelectElement} aField - an or // or