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:
parent
85b2782052
commit
c9b9278092
Notes:
sideshowbarker
2024-07-16 20:51:53 +09:00
Author: https://github.com/trflynn89 Commit: https://github.com/SerenityOS/serenity/commit/c9b9278092 Pull-request: https://github.com/SerenityOS/serenity/pull/20836 Reviewed-by: https://github.com/ADKaster ✅
4 changed files with 154 additions and 98 deletions
|
@ -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;
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue