2023-08-20 16:14:31 -04:00
|
|
|
/*
|
2025-03-20 12:59:44 -04:00
|
|
|
* Copyright (c) 2023-2025, Tim Flynn <trflynn89@ladybird.org>
|
2023-08-20 16:14:31 -04:00
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
|
*/
|
|
|
|
|
|
2025-12-05 10:20:44 -05:00
|
|
|
#include <Application/EventLoopImplementationMacOS.h>
|
2023-08-20 16:14:31 -04:00
|
|
|
#include <LibCore/EventLoop.h>
|
2024-04-28 10:48:29 -04:00
|
|
|
#include <LibCore/ThreadEventQueue.h>
|
2024-08-28 15:45:55 -04:00
|
|
|
#include <Utilities/Conversions.h>
|
2023-08-20 16:14:31 -04:00
|
|
|
|
|
|
|
|
#import <Application/Application.h>
|
2025-09-04 08:53:57 -04:00
|
|
|
#import <Application/ApplicationDelegate.h>
|
|
|
|
|
#import <Interface/LadybirdWebView.h>
|
|
|
|
|
#import <Interface/Tab.h>
|
2025-09-13 09:08:24 -04:00
|
|
|
#import <Interface/TabController.h>
|
2023-08-20 16:14:31 -04:00
|
|
|
|
|
|
|
|
#if !__has_feature(objc_arc)
|
|
|
|
|
# error "This project requires ARC"
|
|
|
|
|
#endif
|
|
|
|
|
|
2024-08-19 19:17:49 -04:00
|
|
|
namespace Ladybird {
|
|
|
|
|
|
2025-06-10 19:13:03 -04:00
|
|
|
Application::Application() = default;
|
2024-08-28 15:45:55 -04:00
|
|
|
|
2025-09-04 08:53:57 -04:00
|
|
|
NonnullOwnPtr<Core::EventLoop> Application::create_platform_event_loop()
|
|
|
|
|
{
|
|
|
|
|
if (!browser_options().headless_mode.has_value()) {
|
2025-12-05 10:20:44 -05:00
|
|
|
Core::EventLoopManager::install(*new EventLoopManagerMacOS);
|
2025-09-04 08:53:57 -04:00
|
|
|
[::Application sharedApplication];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return WebView::Application::create_platform_event_loop();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Optional<WebView::ViewImplementation&> Application::active_web_view() const
|
|
|
|
|
{
|
|
|
|
|
ApplicationDelegate* delegate = [NSApp delegate];
|
|
|
|
|
|
|
|
|
|
if (auto* tab = [delegate activeTab])
|
|
|
|
|
return [[tab web_view] view];
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-13 09:08:24 -04:00
|
|
|
Optional<WebView::ViewImplementation&> Application::open_blank_new_tab(Web::HTML::ActivateTab activate_tab) const
|
|
|
|
|
{
|
|
|
|
|
ApplicationDelegate* delegate = [NSApp delegate];
|
|
|
|
|
|
|
|
|
|
auto* controller = [delegate createNewTab:activate_tab fromTab:[delegate activeTab]];
|
|
|
|
|
auto* tab = (Tab*)[controller window];
|
|
|
|
|
|
|
|
|
|
return [[tab web_view] view];
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-06 15:42:12 -04:00
|
|
|
Optional<ByteString> Application::ask_user_for_download_folder() const
|
|
|
|
|
{
|
|
|
|
|
auto* panel = [NSOpenPanel openPanel];
|
|
|
|
|
[panel setAllowsMultipleSelection:NO];
|
|
|
|
|
[panel setCanChooseDirectories:YES];
|
|
|
|
|
[panel setCanChooseFiles:NO];
|
|
|
|
|
[panel setMessage:@"Select download directory"];
|
2024-08-28 15:45:55 -04:00
|
|
|
|
2025-06-06 15:42:12 -04:00
|
|
|
if ([panel runModal] != NSModalResponseOK)
|
|
|
|
|
return {};
|
2024-08-28 15:45:55 -04:00
|
|
|
|
2025-06-06 15:42:12 -04:00
|
|
|
return Ladybird::ns_string_to_byte_string([[panel URL] path]);
|
|
|
|
|
}
|
2024-08-19 19:17:49 -04:00
|
|
|
|
2025-09-04 08:53:57 -04:00
|
|
|
void Application::display_download_confirmation_dialog(StringView download_name, LexicalPath const& path) const
|
2024-08-19 19:17:49 -04:00
|
|
|
{
|
2025-09-04 08:53:57 -04:00
|
|
|
ApplicationDelegate* delegate = [NSApp delegate];
|
2024-08-19 19:17:49 -04:00
|
|
|
|
2025-09-04 08:53:57 -04:00
|
|
|
auto message = MUST(String::formatted("{} saved to: {}", download_name, path));
|
|
|
|
|
|
|
|
|
|
auto* dialog = [[NSAlert alloc] init];
|
|
|
|
|
[dialog setMessageText:Ladybird::string_to_ns_string(message)];
|
|
|
|
|
[[dialog addButtonWithTitle:@"OK"] setTag:NSModalResponseOK];
|
|
|
|
|
[[dialog addButtonWithTitle:@"Open folder"] setTag:NSModalResponseContinue];
|
|
|
|
|
|
|
|
|
|
__block auto* ns_path = Ladybird::string_to_ns_string(path.string());
|
|
|
|
|
|
|
|
|
|
[dialog beginSheetModalForWindow:[delegate activeTab]
|
|
|
|
|
completionHandler:^(NSModalResponse response) {
|
|
|
|
|
if (response == NSModalResponseContinue) {
|
|
|
|
|
[[NSWorkspace sharedWorkspace] selectFile:ns_path inFileViewerRootedAtPath:@""];
|
|
|
|
|
}
|
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Application::display_error_dialog(StringView error_message) const
|
|
|
|
|
{
|
|
|
|
|
ApplicationDelegate* delegate = [NSApp delegate];
|
|
|
|
|
|
|
|
|
|
auto* dialog = [[NSAlert alloc] init];
|
|
|
|
|
[dialog setMessageText:Ladybird::string_to_ns_string(error_message)];
|
|
|
|
|
|
|
|
|
|
[dialog beginSheetModalForWindow:[delegate activeTab]
|
|
|
|
|
completionHandler:nil];
|
2024-08-19 19:17:49 -04:00
|
|
|
}
|
|
|
|
|
|
2025-09-18 09:18:54 -04:00
|
|
|
Utf16String Application::clipboard_text() const
|
2025-09-18 08:16:36 -04:00
|
|
|
{
|
|
|
|
|
auto* paste_board = [NSPasteboard generalPasteboard];
|
|
|
|
|
|
|
|
|
|
if (auto* contents = [paste_board stringForType:NSPasteboardTypeString])
|
2025-09-18 09:18:54 -04:00
|
|
|
return Ladybird::ns_string_to_utf16_string(contents);
|
2025-09-18 08:16:36 -04:00
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Vector<Web::Clipboard::SystemClipboardRepresentation> Application::clipboard_entries() const
|
|
|
|
|
{
|
|
|
|
|
Vector<Web::Clipboard::SystemClipboardRepresentation> representations;
|
|
|
|
|
auto* paste_board = [NSPasteboard generalPasteboard];
|
|
|
|
|
|
|
|
|
|
for (NSPasteboardType type : [paste_board types]) {
|
|
|
|
|
String mime_type;
|
|
|
|
|
|
|
|
|
|
if (type == NSPasteboardTypeString)
|
|
|
|
|
mime_type = "text/plain"_string;
|
|
|
|
|
else if (type == NSPasteboardTypeHTML)
|
|
|
|
|
mime_type = "text/html"_string;
|
|
|
|
|
else if (type == NSPasteboardTypePNG)
|
|
|
|
|
mime_type = "image/png"_string;
|
2025-10-10 10:31:45 -04:00
|
|
|
else
|
|
|
|
|
continue;
|
2025-09-18 08:16:36 -04:00
|
|
|
|
|
|
|
|
auto data = Ladybird::ns_data_to_string([paste_board dataForType:type]);
|
|
|
|
|
representations.empend(move(data), move(mime_type));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return representations;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Application::insert_clipboard_entry(Web::Clipboard::SystemClipboardRepresentation entry)
|
|
|
|
|
{
|
|
|
|
|
NSPasteboardType pasteboard_type = nil;
|
|
|
|
|
|
|
|
|
|
// https://w3c.github.io/clipboard-apis/#os-specific-well-known-format
|
|
|
|
|
if (entry.mime_type == "text/plain"sv)
|
|
|
|
|
pasteboard_type = NSPasteboardTypeString;
|
|
|
|
|
else if (entry.mime_type == "text/html"sv)
|
|
|
|
|
pasteboard_type = NSPasteboardTypeHTML;
|
|
|
|
|
else if (entry.mime_type == "image/png"sv)
|
|
|
|
|
pasteboard_type = NSPasteboardTypePNG;
|
|
|
|
|
else
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
auto* paste_board = [NSPasteboard generalPasteboard];
|
|
|
|
|
[paste_board clearContents];
|
|
|
|
|
|
|
|
|
|
[paste_board setData:Ladybird::string_to_ns_data(entry.data)
|
|
|
|
|
forType:pasteboard_type];
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-17 10:03:01 -04:00
|
|
|
void Application::on_devtools_enabled() const
|
|
|
|
|
{
|
|
|
|
|
WebView::Application::on_devtools_enabled();
|
|
|
|
|
|
|
|
|
|
ApplicationDelegate* delegate = [NSApp delegate];
|
|
|
|
|
[delegate onDevtoolsEnabled];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Application::on_devtools_disabled() const
|
|
|
|
|
{
|
|
|
|
|
WebView::Application::on_devtools_disabled();
|
|
|
|
|
|
|
|
|
|
ApplicationDelegate* delegate = [NSApp delegate];
|
|
|
|
|
[delegate onDevtoolsDisabled];
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-15 17:39:48 -06:00
|
|
|
}
|
|
|
|
|
|
2025-06-06 15:42:12 -04:00
|
|
|
@interface Application ()
|
2023-08-20 16:14:31 -04:00
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation Application
|
|
|
|
|
|
2024-04-15 17:39:48 -06:00
|
|
|
#pragma mark - NSApplication
|
|
|
|
|
|
2023-08-20 16:14:31 -04:00
|
|
|
- (void)terminate:(id)sender
|
|
|
|
|
{
|
|
|
|
|
Core::EventLoop::current().quit(0);
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-28 10:48:29 -04:00
|
|
|
- (void)sendEvent:(NSEvent*)event
|
|
|
|
|
{
|
|
|
|
|
if ([event type] == NSEventTypeApplicationDefined) {
|
|
|
|
|
Core::ThreadEventQueue::current().process();
|
|
|
|
|
} else {
|
|
|
|
|
[super sendEvent:event];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-20 16:14:31 -04:00
|
|
|
@end
|