From c9b92780921bba2370215fb8a9a71958fee9262c Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Sat, 26 Aug 2023 16:21:22 -0400 Subject: [PATCH] Ladybird: Do not assume the web view is embedded in a normal tab The LadybirdWebView currently assumed it is viewed with a Tab instance. This will not be true with the JavaScript console. This patch removes this assumption by plumbing WebContent callbacks through a new protocol. The Tab interface then implements this protocol. --- Ladybird/AppKit/UI/LadybirdWebView.h | 27 ++++++ Ladybird/AppKit/UI/LadybirdWebView.mm | 92 +++++-------------- Ladybird/AppKit/UI/Tab.h | 6 -- Ladybird/AppKit/UI/Tab.mm | 127 +++++++++++++++++++++----- 4 files changed, 154 insertions(+), 98 deletions(-) diff --git a/Ladybird/AppKit/UI/LadybirdWebView.h b/Ladybird/AppKit/UI/LadybirdWebView.h index dcf72e70b4..943e4fd4b4 100644 --- a/Ladybird/AppKit/UI/LadybirdWebView.h +++ b/Ladybird/AppKit/UI/LadybirdWebView.h @@ -7,15 +7,42 @@ #pragma once #include +#include #include +#include #import +@protocol LadybirdWebViewObserver + +- (String const&)onCreateNewTab:(URL const&)url + activateTab:(Web::HTML::ActivateTab)activate_tab; + +- (String const&)onCreateNewTab:(StringView)html + url:(URL const&)url + activateTab:(Web::HTML::ActivateTab)activate_tab; + +- (void)loadURL:(URL const&)url; +- (void)onLoadStart:(URL const&)url isRedirect:(BOOL)is_redirect; + +- (void)onTitleChange:(DeprecatedString const&)title; +- (void)onFaviconChange:(Gfx::Bitmap const&)bitmap; + +- (void)onNavigateBack; +- (void)onNavigateForward; +- (void)onReload; + +@end + @interface LadybirdWebView : NSClipView +- (instancetype)init:(id)observer; + - (void)loadURL:(URL const&)url; - (void)loadHTML:(StringView)html url:(URL const&)url; +- (String const&)handle; + - (void)handleResize; - (void)handleScroll; - (void)handleVisibility:(BOOL)is_visible; diff --git a/Ladybird/AppKit/UI/LadybirdWebView.mm b/Ladybird/AppKit/UI/LadybirdWebView.mm index 787e21011a..8b8e43619c 100644 --- a/Ladybird/AppKit/UI/LadybirdWebView.mm +++ b/Ladybird/AppKit/UI/LadybirdWebView.mm @@ -15,8 +15,6 @@ #import #import #import -#import -#import #import #if !__has_feature(objc_arc) @@ -53,6 +51,7 @@ struct HideCursor { Optional m_hidden_cursor; } +@property (nonatomic, weak) id observer; @property (nonatomic, strong) NSMenu* page_context_menu; @property (nonatomic, strong) NSMenu* link_context_menu; @property (nonatomic, strong) NSMenu* image_context_menu; @@ -70,9 +69,11 @@ struct HideCursor { @synthesize video_context_menu = _video_context_menu; @synthesize status_label = _status_label; -- (instancetype)init +- (instancetype)init:(id)observer { if (self = [super init]) { + self.observer = observer; + auto* delegate = (ApplicationDelegate*)[NSApp delegate]; auto* screens = [NSScreen screens]; @@ -111,6 +112,11 @@ struct HideCursor { m_web_view_bridge->load_html(html, url); } +- (String const&)handle +{ + return m_web_view_bridge->handle(); +} + - (void)handleResize { [self updateViewportRect:Ladybird::WebViewBridge::ForResize::Yes]; @@ -190,29 +196,19 @@ struct HideCursor { }; m_web_view_bridge->on_new_tab = [self](auto activate_tab) { - auto* delegate = (ApplicationDelegate*)[NSApp delegate]; - - auto* controller = [delegate createNewTab:"about:blank"sv - fromTab:[self tab] - activateTab:activate_tab]; - - auto* tab = (Tab*)[controller window]; - auto* web_view = [tab web_view]; - - return web_view->m_web_view_bridge->handle(); + return [self.observer onCreateNewTab:"about:blank"sv activateTab:activate_tab]; }; m_web_view_bridge->on_activate_tab = [self]() { - [[self tab] orderFront:nil]; + [[self window] orderFront:nil]; }; m_web_view_bridge->on_close = [self]() { - [[self tab] close]; + [[self window] close]; }; m_web_view_bridge->on_load_start = [self](auto const& url, bool is_redirect) { - [[self tabController] onLoadStart:url isRedirect:is_redirect]; - [[self tab] onLoadStart:url]; + [self.observer onLoadStart:url isRedirect:is_redirect]; if (_status_label != nil) { [self.status_label setHidden:YES]; @@ -220,27 +216,11 @@ struct HideCursor { }; m_web_view_bridge->on_title_change = [self](auto const& title) { - [[self tabController] onTitleChange:title]; - - auto* ns_title = Ladybird::string_to_ns_string(title); - [[self tab] onTitleChange:ns_title]; + [self.observer onTitleChange:title]; }; m_web_view_bridge->on_favicon_change = [self](auto const& bitmap) { - static constexpr size_t FAVICON_SIZE = 16; - - auto png = Gfx::PNGWriter::encode(bitmap); - if (png.is_error()) { - return; - } - - auto* data = [NSData dataWithBytes:png.value().data() length:png.value().size()]; - - auto* favicon = [[NSImage alloc] initWithData:data]; - [favicon setResizingMode:NSImageResizingModeStretch]; - [favicon setSize:NSMakeSize(FAVICON_SIZE, FAVICON_SIZE)]; - - [[self tab] onFaviconChange:favicon]; + [self.observer onFaviconChange:bitmap]; }; m_web_view_bridge->on_scroll_to_point = [self](auto position) { @@ -341,15 +321,15 @@ struct HideCursor { }; m_web_view_bridge->on_navigate_back = [self]() { - [[self tabController] navigateBack:nil]; + [self.observer onNavigateBack]; }; m_web_view_bridge->on_navigate_forward = [self]() { - [[self tabController] navigateForward:nil]; + [self.observer onNavigateForward]; }; m_web_view_bridge->on_refresh = [self]() { - [[self tabController] reload:nil]; + [self.observer onReload]; }; m_web_view_bridge->on_enter_tooltip_area = [self](auto, auto const& tooltip) { @@ -374,27 +354,17 @@ struct HideCursor { }; m_web_view_bridge->on_link_click = [self](auto const& url, auto const& target, unsigned modifiers) { - auto* delegate = (ApplicationDelegate*)[NSApp delegate]; - if (modifiers == Mod_Super) { - [delegate createNewTab:url - fromTab:[self tab] - activateTab:Web::HTML::ActivateTab::No]; + [self.observer onCreateNewTab:url activateTab:Web::HTML::ActivateTab::No]; } else if (target == "_blank"sv) { - [delegate createNewTab:url - fromTab:[self tab] - activateTab:Web::HTML::ActivateTab::Yes]; + [self.observer onCreateNewTab:url activateTab:Web::HTML::ActivateTab::Yes]; } else { - [[self tabController] loadURL:url]; + [self.observer loadURL:url]; } }; m_web_view_bridge->on_link_middle_click = [self](auto url, auto, unsigned) { - auto* delegate = (ApplicationDelegate*)[NSApp delegate]; - - [delegate createNewTab:url - fromTab:[self tab] - activateTab:Web::HTML::ActivateTab::No]; + [self.observer onCreateNewTab:url activateTab:Web::HTML::ActivateTab::No]; }; m_web_view_bridge->on_context_menu_request = [self](auto position) { @@ -605,26 +575,14 @@ struct HideCursor { }; m_web_view_bridge->on_received_source = [self](auto const& url, auto const& source) { - auto* delegate = (ApplicationDelegate*)[NSApp delegate]; auto html = WebView::highlight_source(url, source); - [delegate createNewTab:html - url:url - fromTab:[self tab] - activateTab:Web::HTML::ActivateTab::Yes]; + [self.observer onCreateNewTab:html + url:url + activateTab:Web::HTML::ActivateTab::Yes]; }; } -- (Tab*)tab -{ - return (Tab*)[self window]; -} - -- (TabController*)tabController -{ - return (TabController*)[[self tab] windowController]; -} - - (NSScrollView*)scrollView { return (NSScrollView*)[self superview]; diff --git a/Ladybird/AppKit/UI/Tab.h b/Ladybird/AppKit/UI/Tab.h index 172d200713..fad691f05a 100644 --- a/Ladybird/AppKit/UI/Tab.h +++ b/Ladybird/AppKit/UI/Tab.h @@ -6,18 +6,12 @@ #pragma once -#include - #import @class LadybirdWebView; @interface Tab : NSWindow -- (void)onLoadStart:(URL const&)url; -- (void)onTitleChange:(NSString*)title; -- (void)onFaviconChange:(NSImage*)favicon; - @property (nonatomic, strong) LadybirdWebView* web_view; @end diff --git a/Ladybird/AppKit/UI/Tab.mm b/Ladybird/AppKit/UI/Tab.mm index 0371173973..7d63fb4dac 100644 --- a/Ladybird/AppKit/UI/Tab.mm +++ b/Ladybird/AppKit/UI/Tab.mm @@ -4,13 +4,19 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include +#include +#include +#include +#include +#include + +#import #import #import #import #import -#include - #if !__has_feature(objc_arc) # error "This project requires ARC" #endif @@ -18,7 +24,7 @@ static constexpr CGFloat const WINDOW_WIDTH = 1000; static constexpr CGFloat const WINDOW_HEIGHT = 800; -@interface Tab () +@interface Tab () @property (nonatomic, strong) NSString* title; @property (nonatomic, strong) NSImage* favicon; @@ -59,7 +65,7 @@ static constexpr CGFloat const WINDOW_HEIGHT = 800; defer:NO]; if (self) { - self.web_view = [[LadybirdWebView alloc] init]; + self.web_view = [[LadybirdWebView alloc] init:self]; [self.web_view setPostsBoundsChangedNotifications:YES]; self.favicon = [Tab defaultFavicon]; @@ -89,29 +95,13 @@ static constexpr CGFloat const WINDOW_HEIGHT = 800; return self; } -#pragma mark - Public methods - -- (void)onLoadStart:(URL const&)url -{ - self.title = Ladybird::string_to_ns_string(url.serialize()); - self.favicon = [Tab defaultFavicon]; - [self updateTabTitleAndFavicon]; -} - -- (void)onTitleChange:(NSString*)title -{ - self.title = title; - [self updateTabTitleAndFavicon]; -} - -- (void)onFaviconChange:(NSImage*)favicon -{ - self.favicon = favicon; - [self updateTabTitleAndFavicon]; -} - #pragma mark - Private methods +- (TabController*)tabController +{ + return (TabController*)[self windowController]; +} + - (void)updateTabTitleAndFavicon { auto* favicon_attachment = [[NSTextAttachment alloc] init]; @@ -153,6 +143,93 @@ static constexpr CGFloat const WINDOW_HEIGHT = 800; [[self web_view] handleScroll]; } +#pragma mark - LadybirdWebViewObserver + +- (String const&)onCreateNewTab:(URL const&)url + activateTab:(Web::HTML::ActivateTab)activate_tab +{ + auto* delegate = (ApplicationDelegate*)[NSApp delegate]; + + auto* controller = [delegate createNewTab:url + fromTab:self + activateTab:activate_tab]; + + auto* tab = (Tab*)[controller window]; + return [[tab web_view] handle]; +} + +- (String const&)onCreateNewTab:(StringView)html + url:(URL const&)url + activateTab:(Web::HTML::ActivateTab)activate_tab +{ + auto* delegate = (ApplicationDelegate*)[NSApp delegate]; + + auto* controller = [delegate createNewTab:html + url:url + fromTab:self + activateTab:activate_tab]; + + auto* tab = (Tab*)[controller window]; + return [[tab web_view] handle]; +} + +- (void)loadURL:(URL const&)url +{ + [[self tabController] loadURL:url]; +} + +- (void)onLoadStart:(URL const&)url isRedirect:(BOOL)is_redirect +{ + [[self tabController] onLoadStart:url isRedirect:is_redirect]; + + self.title = Ladybird::string_to_ns_string(url.serialize()); + self.favicon = [Tab defaultFavicon]; + [self updateTabTitleAndFavicon]; +} + +- (void)onTitleChange:(DeprecatedString const&)title +{ + [[self tabController] onTitleChange:title]; + + self.title = Ladybird::string_to_ns_string(title); + [self updateTabTitleAndFavicon]; +} + +- (void)onFaviconChange:(Gfx::Bitmap const&)bitmap +{ + static constexpr size_t FAVICON_SIZE = 16; + + auto png = Gfx::PNGWriter::encode(bitmap); + if (png.is_error()) { + return; + } + + auto* data = [NSData dataWithBytes:png.value().data() + length:png.value().size()]; + + auto* favicon = [[NSImage alloc] initWithData:data]; + [favicon setResizingMode:NSImageResizingModeStretch]; + [favicon setSize:NSMakeSize(FAVICON_SIZE, FAVICON_SIZE)]; + + self.favicon = favicon; + [self updateTabTitleAndFavicon]; +} + +- (void)onNavigateBack +{ + [[self tabController] navigateBack:nil]; +} + +- (void)onNavigateForward +{ + [[self tabController] navigateForward:nil]; +} + +- (void)onReload +{ + [[self tabController] reload:nil]; +} + #pragma mark - NSWindow - (void)setIsVisible:(BOOL)flag