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.
This commit is contained in:
Timothy Flynn 2023-08-26 16:21:22 -04:00 committed by Andrew Kaster
parent 85b2782052
commit c9b9278092
Notes: sideshowbarker 2024-07-16 20:51:53 +09:00
4 changed files with 154 additions and 98 deletions

View file

@ -7,15 +7,42 @@
#pragma once
#include <AK/Forward.h>
#include <LibGfx/Forward.h>
#include <LibWeb/CSS/PreferredColorScheme.h>
#include <LibWeb/HTML/ActivateTab.h>
#import <System/Cocoa.h>
@protocol LadybirdWebViewObserver <NSObject>
- (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<LadybirdWebViewObserver>)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;

View file

@ -15,8 +15,6 @@
#import <Application/ApplicationDelegate.h>
#import <UI/Event.h>
#import <UI/LadybirdWebView.h>
#import <UI/Tab.h>
#import <UI/TabController.h>
#import <Utilities/Conversions.h>
#if !__has_feature(objc_arc)
@ -53,6 +51,7 @@ struct HideCursor {
Optional<HideCursor> m_hidden_cursor;
}
@property (nonatomic, weak) id<LadybirdWebViewObserver> 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<LadybirdWebViewObserver>)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];

View file

@ -6,18 +6,12 @@
#pragma once
#include <AK/URL.h>
#import <System/Cocoa.h>
@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

View file

@ -4,13 +4,19 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/DeprecatedString.h>
#include <AK/String.h>
#include <AK/URL.h>
#include <Ladybird/Utilities.h>
#include <LibGfx/ImageFormats/PNGWriter.h>
#include <LibGfx/ShareableBitmap.h>
#import <Application/ApplicationDelegate.h>
#import <UI/LadybirdWebView.h>
#import <UI/Tab.h>
#import <UI/TabController.h>
#import <Utilities/Conversions.h>
#include <Ladybird/Utilities.h>
#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 () <LadybirdWebViewObserver>
@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