From 7108c7850aa51234e5a40c7ada8abe9445ba0fdd Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Wed, 4 Sep 2024 14:47:07 +0500 Subject: [PATCH] Fix exception on Copy in Save dialog (#3205) Task/Issue URL: https://app.asana.com/0/1204468785277077/1203447555037784/f --- DuckDuckGo.xcodeproj/project.pbxproj | 6 --- DuckDuckGo/Application/Application.swift | 4 +- DuckDuckGo/Application/CopyHandler.swift | 44 ------------------- DuckDuckGo/Menus/MainMenu.swift | 10 ++--- .../View/AddressBarTextEditor.swift | 25 ++++++++++- UnitTests/Menus/MainMenuTests.swift | 2 +- 6 files changed, 31 insertions(+), 60 deletions(-) delete mode 100644 DuckDuckGo/Application/CopyHandler.swift diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index c41c4f9db9..535b452e36 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -350,7 +350,6 @@ 3706FAD6293F65D500E42796 /* ConfigurationStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85480FCE25D1AA22009424E3 /* ConfigurationStore.swift */; }; 3706FAD7293F65D500E42796 /* Feedback.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3D531A27A2F57E00074EC1 /* Feedback.swift */; }; 3706FAD9293F65D500E42796 /* FirefoxFaviconsReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B0A63E7289DB58E00378EF7 /* FirefoxFaviconsReader.swift */; }; - 3706FADA293F65D500E42796 /* CopyHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 858A798226A8B75F00A75A42 /* CopyHandler.swift */; }; 3706FADB293F65D500E42796 /* ContentBlockingRulesUpdateObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E7E2E8F29029A2A00C01B54 /* ContentBlockingRulesUpdateObserver.swift */; }; 3706FADC293F65D500E42796 /* FirefoxLoginReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B8AC93826B48A5100879451 /* FirefoxLoginReader.swift */; }; 3706FADD293F65D500E42796 /* AtbParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = B69B50382726A12400758A2B /* AtbParser.swift */; }; @@ -1758,7 +1757,6 @@ 8589063A267BCD8E00D23B0D /* SaveCredentialsPopover.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85890639267BCD8E00D23B0D /* SaveCredentialsPopover.swift */; }; 8589063C267BCDC000D23B0D /* SaveCredentialsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8589063B267BCDC000D23B0D /* SaveCredentialsViewController.swift */; }; 858A797F26A79EAA00A75A42 /* UserText+PasswordManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 858A797E26A79EAA00A75A42 /* UserText+PasswordManager.swift */; }; - 858A798326A8B75F00A75A42 /* CopyHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 858A798226A8B75F00A75A42 /* CopyHandler.swift */; }; 858A798526A8BB5D00A75A42 /* NSTextViewExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 858A798426A8BB5D00A75A42 /* NSTextViewExtension.swift */; }; 858A798826A99DBE00A75A42 /* PasswordManagementItemListModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 858A798726A99DBE00A75A42 /* PasswordManagementItemListModelTests.swift */; }; 858A798A26A9B35E00A75A42 /* PasswordManagementItemModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 858A798926A9B35E00A75A42 /* PasswordManagementItemModelTests.swift */; }; @@ -3737,7 +3735,6 @@ 85890639267BCD8E00D23B0D /* SaveCredentialsPopover.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveCredentialsPopover.swift; sourceTree = ""; }; 8589063B267BCDC000D23B0D /* SaveCredentialsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveCredentialsViewController.swift; sourceTree = ""; }; 858A797E26A79EAA00A75A42 /* UserText+PasswordManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserText+PasswordManager.swift"; sourceTree = ""; }; - 858A798226A8B75F00A75A42 /* CopyHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyHandler.swift; sourceTree = ""; }; 858A798426A8BB5D00A75A42 /* NSTextViewExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSTextViewExtension.swift; sourceTree = ""; }; 858A798726A99DBE00A75A42 /* PasswordManagementItemListModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordManagementItemListModelTests.swift; sourceTree = ""; }; 858A798926A9B35E00A75A42 /* PasswordManagementItemModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordManagementItemModelTests.swift; sourceTree = ""; }; @@ -6979,7 +6976,6 @@ AA585D81248FD31100E9A3E2 /* AppDelegate.swift */, AA4D700625545EF800C3411E /* URLEventHandler.swift */, AA4FF40B2624751A004E2377 /* GrammarFeaturesManager.swift */, - 858A798226A8B75F00A75A42 /* CopyHandler.swift */, 1D36E65A298ACD2900AA485D /* AppIconChanger.swift */, CB24F70B29A3D9CB006DCC58 /* AppConfigurationURLProvider.swift */, 1D4071AD2BD64266002D4537 /* DockCustomizer.swift */, @@ -10226,7 +10222,6 @@ 1D0DE9422C3BB9CC0037ABC2 /* ReleaseNotesParser.swift in Sources */, 3707C722294B5D2900682A9F /* WKWebViewExtension.swift in Sources */, 3706FAD9293F65D500E42796 /* FirefoxFaviconsReader.swift in Sources */, - 3706FADA293F65D500E42796 /* CopyHandler.swift in Sources */, 3706FADB293F65D500E42796 /* ContentBlockingRulesUpdateObserver.swift in Sources */, 3706FADC293F65D500E42796 /* FirefoxLoginReader.swift in Sources */, 3706FADD293F65D500E42796 /* AtbParser.swift in Sources */, @@ -11721,7 +11716,6 @@ 85480FCF25D1AA22009424E3 /* ConfigurationStore.swift in Sources */, AA3D531B27A2F57E00074EC1 /* Feedback.swift in Sources */, 4B0A63E8289DB58E00378EF7 /* FirefoxFaviconsReader.swift in Sources */, - 858A798326A8B75F00A75A42 /* CopyHandler.swift in Sources */, 1E7E2E9029029A2A00C01B54 /* ContentBlockingRulesUpdateObserver.swift in Sources */, 4B8AC93926B48A5100879451 /* FirefoxLoginReader.swift in Sources */, F18826902BC0105800D9AC4F /* PixelDataRecord.swift in Sources */, diff --git a/DuckDuckGo/Application/Application.swift b/DuckDuckGo/Application/Application.swift index 09b13d6c87..cecfff3a7e 100644 --- a/DuckDuckGo/Application/Application.swift +++ b/DuckDuckGo/Application/Application.swift @@ -22,7 +22,6 @@ import Foundation @objc(Application) final class Application: NSApplication { - private let copyHandler = CopyHandler() public static var appDelegate: AppDelegate! override init() { @@ -34,8 +33,7 @@ final class Application: NSApplication { let mainMenu = MainMenu(featureFlagger: delegate.featureFlagger, bookmarkManager: delegate.bookmarksManager, - faviconManager: FaviconManager.shared, - copyHandler: copyHandler) + faviconManager: FaviconManager.shared) self.mainMenu = mainMenu // Makes sure Spotlight search is part of Help menu diff --git a/DuckDuckGo/Application/CopyHandler.swift b/DuckDuckGo/Application/CopyHandler.swift deleted file mode 100644 index 4fab23bac5..0000000000 --- a/DuckDuckGo/Application/CopyHandler.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// CopyHandler.swift -// -// Copyright © 2021 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation - -final class CopyHandler: NSObject { - - static let copySelector = #selector(Self.copy(_:)) - - @IBAction func copy(_ sender: Any?) { - guard let responder = NSApp.keyWindow?.firstResponder else { return } - guard let editor = responder as? AddressBarTextEditor else { - NSApp.sendAction(Self.copySelector, to: responder, from: self) - return - } - - let selectedText = editor.selectedText - - NSPasteboard.general.clearContents() - NSPasteboard.general.setString(selectedText, forType: .string) - - if let url = URL(trimmedAddressBarString: selectedText.trimmingWhitespace()) { - NSPasteboard.general.copy(url, withString: selectedText) - } else { - NSPasteboard.general.copy(selectedText) - } - } - -} diff --git a/DuckDuckGo/Menus/MainMenu.swift b/DuckDuckGo/Menus/MainMenu.swift index 0b47323d86..ef62fba0fe 100644 --- a/DuckDuckGo/Menus/MainMenu.swift +++ b/DuckDuckGo/Menus/MainMenu.swift @@ -106,14 +106,14 @@ final class MainMenu: NSMenu { // MARK: - Initialization @MainActor - init(featureFlagger: FeatureFlagger, bookmarkManager: BookmarkManager, faviconManager: FaviconManagement, copyHandler: CopyHandler) { + init(featureFlagger: FeatureFlagger, bookmarkManager: BookmarkManager, faviconManager: FaviconManagement) { super.init(title: UserText.duckDuckGo) buildItems { buildDuckDuckGoMenu() buildFileMenu() - buildEditMenu(copyHandler: copyHandler) + buildEditMenu() buildViewMenu() buildHistoryMenu() buildBookmarksMenu() @@ -182,14 +182,14 @@ final class MainMenu: NSMenu { } } - func buildEditMenu(copyHandler: CopyHandler) -> NSMenuItem { + func buildEditMenu() -> NSMenuItem { NSMenuItem(title: UserText.mainMenuEdit) { NSMenuItem(title: UserText.mainMenuEditUndo, action: Selector(("undo:")), keyEquivalent: "z") NSMenuItem(title: UserText.mainMenuEditRedo, action: Selector(("redo:")), keyEquivalent: "Z") NSMenuItem.separator() - NSMenuItem(title: UserText.mainMenuEditCut, action: #selector(NSText.cut), keyEquivalent: "x") - NSMenuItem(title: UserText.mainMenuEditCopy, action: #selector(CopyHandler.copy(_:)), target: copyHandler, keyEquivalent: "c") + NSMenuItem(title: UserText.mainMenuEditCut, action: #selector(NSText.cut(_:)), keyEquivalent: "x") + NSMenuItem(title: UserText.mainMenuEditCopy, action: #selector(NSText.copy(_:)), keyEquivalent: "c") NSMenuItem(title: UserText.mainMenuEditPaste, action: #selector(NSText.paste), keyEquivalent: "v") NSMenuItem(title: UserText.mainMenuEditPasteAndMatchStyle, action: #selector(NSTextView.pasteAsPlainText), keyEquivalent: [.option, .command, .shift, "v"]) NSMenuItem(title: UserText.mainMenuEditPasteAndMatchStyle, action: #selector(NSTextView.pasteAsPlainText), keyEquivalent: [.command, .shift, "v"]) diff --git a/DuckDuckGo/NavigationBar/View/AddressBarTextEditor.swift b/DuckDuckGo/NavigationBar/View/AddressBarTextEditor.swift index c235f193d1..64bc043bc1 100644 --- a/DuckDuckGo/NavigationBar/View/AddressBarTextEditor.swift +++ b/DuckDuckGo/NavigationBar/View/AddressBarTextEditor.swift @@ -147,7 +147,30 @@ final class AddressBarTextEditor: NSTextView { // MARK: - Copy/Paste override func copy(_ sender: Any?) { - CopyHandler().copy(sender) + let selectedText = self.selectedText + guard !selectedText.isEmpty else { + super.copy(sender) + return + } + + NSPasteboard.general.clearContents() + NSPasteboard.general.setString(selectedText, forType: .string) + + if let url = URL(trimmedAddressBarString: selectedText.trimmingWhitespace()) { + NSPasteboard.general.copy(url, withString: selectedText) + } else { + NSPasteboard.general.copy(selectedText) + } + } + + override func cut(_ sender: Any?) { + guard !self.selectedText.isEmpty else { + super.cut(sender) + return + } + + copy(sender) + self.delete(sender) } override func paste(_ sender: Any?) { diff --git a/UnitTests/Menus/MainMenuTests.swift b/UnitTests/Menus/MainMenuTests.swift index b3b5f4275e..21851053c4 100644 --- a/UnitTests/Menus/MainMenuTests.swift +++ b/UnitTests/Menus/MainMenuTests.swift @@ -94,7 +94,7 @@ class MainMenuTests: XCTestCase { @MainActor func testWhenBookmarksMenuIsInitialized_ThenSecondItemIsBookmarkAllTabs() throws { // GIVEN - let sut = MainMenu(featureFlagger: DummyFeatureFlagger(), bookmarkManager: MockBookmarkManager(), faviconManager: FaviconManagerMock(), copyHandler: CopyHandler()) + let sut = MainMenu(featureFlagger: DummyFeatureFlagger(), bookmarkManager: MockBookmarkManager(), faviconManager: FaviconManagerMock()) let bookmarksMenu = try XCTUnwrap(sut.item(withTitle: UserText.bookmarks)) // WHEN