Merge remote-tracking branch 'origin/main' into issue-296-access-control-modes
This commit is contained in:
commit
c7f10a7c44
2 changed files with 131 additions and 1 deletions
|
|
@ -20,6 +20,8 @@ final class CmuxWebView: WKWebView {
|
|||
private static var contextMenuFallbackKey: UInt8 = 0
|
||||
|
||||
var onContextMenuDownloadStateChanged: ((Bool) -> Void)?
|
||||
var contextMenuLinkURLProvider: ((CmuxWebView, NSPoint, @escaping (URL?) -> Void) -> Void)?
|
||||
var contextMenuDefaultBrowserOpener: ((URL) -> Bool)?
|
||||
|
||||
override func performKeyEquivalent(with event: NSEvent) -> Bool {
|
||||
// Preserve Cmd+Return/Enter for web content (e.g. editors/forms). Do not
|
||||
|
|
@ -302,6 +304,27 @@ final class CmuxWebView: WKWebView {
|
|||
}
|
||||
}
|
||||
|
||||
private func resolveContextMenuLinkURL(at point: NSPoint, completion: @escaping (URL?) -> Void) {
|
||||
if let contextMenuLinkURLProvider {
|
||||
contextMenuLinkURLProvider(self, point, completion)
|
||||
return
|
||||
}
|
||||
findLinkURLAtPoint(point, completion: completion)
|
||||
}
|
||||
|
||||
private func canOpenInDefaultBrowser(_ url: URL) -> Bool {
|
||||
let scheme = url.scheme?.lowercased() ?? ""
|
||||
return scheme == "http" || scheme == "https"
|
||||
}
|
||||
|
||||
private func openContextMenuLinkInDefaultBrowser(_ url: URL) {
|
||||
if let contextMenuDefaultBrowserOpener {
|
||||
_ = contextMenuDefaultBrowserOpener(url)
|
||||
return
|
||||
}
|
||||
_ = NSWorkspace.shared.open(url)
|
||||
}
|
||||
|
||||
private func runContextMenuFallback(action: Selector?, target: AnyObject?, sender: Any?) {
|
||||
guard let action else { return }
|
||||
// Guard against accidental self-recursion if fallback gets overwritten.
|
||||
|
|
@ -452,8 +475,22 @@ final class CmuxWebView: WKWebView {
|
|||
override func willOpenMenu(_ menu: NSMenu, with event: NSEvent) {
|
||||
super.willOpenMenu(menu, with: event)
|
||||
lastContextMenuPoint = convert(event.locationInWindow, from: nil)
|
||||
var openLinkInsertionIndex: Int?
|
||||
var hasDefaultBrowserOpenLinkItem = false
|
||||
|
||||
for (index, item) in menu.items.enumerated() {
|
||||
if !hasDefaultBrowserOpenLinkItem,
|
||||
(item.action == #selector(contextMenuOpenLinkInDefaultBrowser(_:))
|
||||
|| item.title == "Open Link in Default Browser") {
|
||||
hasDefaultBrowserOpenLinkItem = true
|
||||
}
|
||||
|
||||
if openLinkInsertionIndex == nil,
|
||||
(item.identifier?.rawValue == "WKMenuItemIdentifierOpenLink"
|
||||
|| item.title == "Open Link") {
|
||||
openLinkInsertionIndex = index + 1
|
||||
}
|
||||
|
||||
for item in menu.items {
|
||||
// Rename "Open Link in New Window" to "Open Link in New Tab".
|
||||
// The UIDelegate's createWebViewWith already handles the action
|
||||
// by opening the link as a new surface in the same pane.
|
||||
|
|
@ -494,6 +531,25 @@ final class CmuxWebView: WKWebView {
|
|||
item.action = #selector(contextMenuDownloadLinkedFile(_:))
|
||||
}
|
||||
}
|
||||
|
||||
if let openLinkInsertionIndex, !hasDefaultBrowserOpenLinkItem {
|
||||
let item = NSMenuItem(
|
||||
title: "Open Link in Default Browser",
|
||||
action: #selector(contextMenuOpenLinkInDefaultBrowser(_:)),
|
||||
keyEquivalent: ""
|
||||
)
|
||||
item.target = self
|
||||
menu.insertItem(item, at: min(openLinkInsertionIndex, menu.items.count))
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func contextMenuOpenLinkInDefaultBrowser(_ sender: Any?) {
|
||||
_ = sender
|
||||
let point = lastContextMenuPoint
|
||||
resolveContextMenuLinkURL(at: point) { [weak self] url in
|
||||
guard let self, let url, self.canOpenInDefaultBrowser(url) else { return }
|
||||
self.openContextMenuLinkInDefaultBrowser(url)
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func contextMenuDownloadImage(_ sender: Any?) {
|
||||
|
|
|
|||
|
|
@ -132,6 +132,80 @@ final class CmuxWebViewKeyEquivalentTests: XCTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
final class CmuxWebViewContextMenuTests: XCTestCase {
|
||||
private func makeRightMouseDownEvent() -> NSEvent {
|
||||
guard let event = NSEvent.mouseEvent(
|
||||
with: .rightMouseDown,
|
||||
location: .zero,
|
||||
modifierFlags: [],
|
||||
timestamp: ProcessInfo.processInfo.systemUptime,
|
||||
windowNumber: 0,
|
||||
context: nil,
|
||||
eventNumber: 0,
|
||||
clickCount: 1,
|
||||
pressure: 1.0
|
||||
) else {
|
||||
fatalError("Failed to create rightMouseDown event")
|
||||
}
|
||||
return event
|
||||
}
|
||||
|
||||
func testWillOpenMenuAddsOpenLinkInDefaultBrowserAndRoutesSelectionToDefaultBrowserOpener() {
|
||||
_ = NSApplication.shared
|
||||
let webView = CmuxWebView(frame: NSRect(x: 0, y: 0, width: 800, height: 600), configuration: WKWebViewConfiguration())
|
||||
let menu = NSMenu()
|
||||
let openLinkItem = NSMenuItem(title: "Open Link", action: nil, keyEquivalent: "")
|
||||
openLinkItem.identifier = NSUserInterfaceItemIdentifier("WKMenuItemIdentifierOpenLink")
|
||||
menu.addItem(openLinkItem)
|
||||
menu.addItem(NSMenuItem(title: "Copy Link", action: nil, keyEquivalent: ""))
|
||||
|
||||
var openedURL: URL?
|
||||
webView.contextMenuLinkURLProvider = { _, _, completion in
|
||||
completion(URL(string: "https://example.com/docs")!)
|
||||
}
|
||||
webView.contextMenuDefaultBrowserOpener = { url in
|
||||
openedURL = url
|
||||
return true
|
||||
}
|
||||
|
||||
webView.willOpenMenu(menu, with: makeRightMouseDownEvent())
|
||||
|
||||
guard let defaultBrowserItemIndex = menu.items.firstIndex(where: { $0.title == "Open Link in Default Browser" }) else {
|
||||
XCTFail("Expected Open Link in Default Browser item in context menu")
|
||||
return
|
||||
}
|
||||
guard let openLinkIndex = menu.items.firstIndex(where: { $0.identifier?.rawValue == "WKMenuItemIdentifierOpenLink" }) else {
|
||||
XCTFail("Expected Open Link item in context menu")
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssertEqual(defaultBrowserItemIndex, openLinkIndex + 1)
|
||||
let defaultBrowserItem = menu.items[defaultBrowserItemIndex]
|
||||
XCTAssertTrue(defaultBrowserItem.target === webView)
|
||||
XCTAssertNotNil(defaultBrowserItem.action)
|
||||
|
||||
let dispatched = NSApp.sendAction(
|
||||
defaultBrowserItem.action!,
|
||||
to: defaultBrowserItem.target,
|
||||
from: defaultBrowserItem
|
||||
)
|
||||
XCTAssertTrue(dispatched)
|
||||
XCTAssertEqual(openedURL?.absoluteString, "https://example.com/docs")
|
||||
}
|
||||
|
||||
func testWillOpenMenuSkipsDefaultBrowserItemWhenContextHasNoOpenLinkEntry() {
|
||||
let webView = CmuxWebView(frame: .zero, configuration: WKWebViewConfiguration())
|
||||
let menu = NSMenu()
|
||||
menu.addItem(NSMenuItem(title: "Back", action: nil, keyEquivalent: ""))
|
||||
menu.addItem(NSMenuItem(title: "Forward", action: nil, keyEquivalent: ""))
|
||||
|
||||
webView.willOpenMenu(menu, with: makeRightMouseDownEvent())
|
||||
|
||||
XCTAssertFalse(menu.items.contains { $0.title == "Open Link in Default Browser" })
|
||||
}
|
||||
}
|
||||
|
||||
final class BrowserDevToolsButtonDebugSettingsTests: XCTestCase {
|
||||
private func makeIsolatedDefaults() -> UserDefaults {
|
||||
let suiteName = "BrowserDevToolsButtonDebugSettingsTests.\(UUID().uuidString)"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue