mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-09-11 05:25:32 +03:00
LibWebView+UI: Migrate Ladybird's command line flags to LibWebView
Currently, if we want to add a new e.g. WebContent command line option, we have to add it to all of Qt, AppKit, and headless-browser. (Or worse, we only add it to one of these, and we have feature disparity). To prevent this, this moves command line flags to WebView::Application. The flags are assigned to ChromeOptions and WebContentOptions structs. Each chrome can still add its platform-specific options; for example, the Qt chrome has a flag to enable Qt networking. There should be no behavior change here, other than that AppKit will now support command line flags that were previously only supported by Qt.
This commit is contained in:
parent
0e640f6f70
commit
5f8d852dae
Notes:
github-actions[bot]
2024-08-01 09:39:41 +00:00
Author: https://github.com/trflynn89 Commit: https://github.com/LadybirdBrowser/ladybird/commit/5f8d852daea Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/910 Reviewed-by: https://github.com/ADKaster
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
|
||||
* Copyright (c) 2023-2024, Tim Flynn <trflynn89@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
@ -7,8 +7,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <AK/Error.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibIPC/Forward.h>
|
||||
#include <LibMain/Main.h>
|
||||
#include <LibURL/URL.h>
|
||||
#include <LibWebView/Forward.h>
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
@ -19,9 +20,10 @@ class WebViewBridge;
|
||||
|
||||
@interface Application : NSApplication
|
||||
|
||||
- (instancetype)init;
|
||||
- (void)setupWebViewApplication:(Main::Arguments&)arguments
|
||||
newTabPageURL:(URL::URL)new_tab_page_url;
|
||||
|
||||
- (ErrorOr<void>)launchRequestServer:(Vector<ByteString> const&)certificates;
|
||||
- (ErrorOr<void>)launchRequestServer;
|
||||
- (ErrorOr<void>)launchImageDecoder;
|
||||
- (ErrorOr<NonnullRefPtr<WebView::WebContentClient>>)launchWebContent:(Ladybird::WebViewBridge&)web_view_bridge;
|
||||
- (ErrorOr<IPC::File>)launchWebWorker;
|
||||
|
@ -1,10 +1,9 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
|
||||
* Copyright (c) 2023-2024, Tim Flynn <trflynn89@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <Application/ApplicationBridge.h>
|
||||
#include <LibCore/EventLoop.h>
|
||||
#include <LibCore/ThreadEventQueue.h>
|
||||
@ -25,20 +24,17 @@
|
||||
|
||||
@implementation Application
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
m_application_bridge = make<Ladybird::ApplicationBridge>();
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Public methods
|
||||
|
||||
- (ErrorOr<void>)launchRequestServer:(Vector<ByteString> const&)certificates
|
||||
- (void)setupWebViewApplication:(Main::Arguments&)arguments
|
||||
newTabPageURL:(URL::URL)new_tab_page_url
|
||||
{
|
||||
return m_application_bridge->launch_request_server(certificates);
|
||||
m_application_bridge = Ladybird::ApplicationBridge::create(arguments, move(new_tab_page_url));
|
||||
}
|
||||
|
||||
- (ErrorOr<void>)launchRequestServer
|
||||
{
|
||||
return m_application_bridge->launch_request_server();
|
||||
}
|
||||
|
||||
- (ErrorOr<void>)launchImageDecoder
|
||||
|
@ -4,7 +4,6 @@
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <Application/ApplicationBridge.h>
|
||||
#include <Ladybird/AppKit/UI/LadybirdWebViewBridge.h>
|
||||
#include <Ladybird/HelperProcess.h>
|
||||
@ -23,17 +22,17 @@ struct ApplicationBridgeImpl {
|
||||
RefPtr<ImageDecoderClient::Client> image_decoder_client;
|
||||
};
|
||||
|
||||
ApplicationBridge::ApplicationBridge()
|
||||
ApplicationBridge::ApplicationBridge(Badge<WebView::Application>, Main::Arguments&)
|
||||
: m_impl(make<ApplicationBridgeImpl>())
|
||||
{
|
||||
}
|
||||
|
||||
ApplicationBridge::~ApplicationBridge() = default;
|
||||
|
||||
ErrorOr<void> ApplicationBridge::launch_request_server(Vector<ByteString> const& certificates)
|
||||
ErrorOr<void> ApplicationBridge::launch_request_server()
|
||||
{
|
||||
auto request_server_paths = TRY(get_paths_for_helper_process("RequestServer"sv));
|
||||
auto protocol_client = TRY(launch_request_server_process(request_server_paths, s_ladybird_resource_root, certificates));
|
||||
auto protocol_client = TRY(launch_request_server_process(request_server_paths, s_ladybird_resource_root));
|
||||
|
||||
m_impl->request_server_client = move(protocol_client);
|
||||
return {};
|
||||
@ -79,7 +78,7 @@ ErrorOr<NonnullRefPtr<WebView::WebContentClient>> ApplicationBridge::launch_web_
|
||||
auto image_decoder_socket = TRY(connect_new_image_decoder_client(*m_impl->image_decoder_client));
|
||||
|
||||
auto web_content_paths = TRY(get_paths_for_helper_process("WebContent"sv));
|
||||
auto web_content = TRY(launch_web_content_process(web_view_bridge, web_content_paths, web_view_bridge.web_content_options(), move(image_decoder_socket), move(request_server_socket)));
|
||||
auto web_content = TRY(launch_web_content_process(web_view_bridge, web_content_paths, move(image_decoder_socket), move(request_server_socket)));
|
||||
|
||||
return web_content;
|
||||
}
|
||||
|
@ -7,8 +7,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibIPC/Forward.h>
|
||||
#include <LibWebView/Application.h>
|
||||
#include <LibWebView/Forward.h>
|
||||
|
||||
namespace Ladybird {
|
||||
@ -16,12 +16,13 @@ namespace Ladybird {
|
||||
struct ApplicationBridgeImpl;
|
||||
class WebViewBridge;
|
||||
|
||||
class ApplicationBridge {
|
||||
class ApplicationBridge : public WebView::Application {
|
||||
WEB_VIEW_APPLICATION(ApplicationBridge)
|
||||
|
||||
public:
|
||||
ApplicationBridge();
|
||||
~ApplicationBridge();
|
||||
|
||||
ErrorOr<void> launch_request_server(Vector<ByteString> const& certificates);
|
||||
ErrorOr<void> launch_request_server();
|
||||
ErrorOr<void> launch_image_decoder();
|
||||
ErrorOr<NonnullRefPtr<WebView::WebContentClient>> launch_web_content(WebViewBridge&);
|
||||
ErrorOr<IPC::File> launch_web_worker();
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
|
||||
* Copyright (c) 2023-2024, Tim Flynn <trflynn89@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
@ -8,8 +8,6 @@
|
||||
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <Ladybird/Types.h>
|
||||
#include <LibURL/URL.h>
|
||||
#include <LibWeb/CSS/PreferredColorScheme.h>
|
||||
#include <LibWeb/CSS/PreferredContrast.h>
|
||||
@ -24,12 +22,7 @@
|
||||
|
||||
@interface ApplicationDelegate : NSObject <NSApplicationDelegate>
|
||||
|
||||
- (nullable instancetype)init:(Vector<URL::URL>)initial_urls
|
||||
newTabPageURL:(URL::URL)new_tab_page_url
|
||||
withCookieJar:(NonnullOwnPtr<WebView::CookieJar>)cookie_jar
|
||||
webContentOptions:(Ladybird::WebContentOptions const&)web_content_options
|
||||
webdriverContentIPCPath:(StringView)webdriver_content_ipc_path
|
||||
allowPopups:(BOOL)allow_popups;
|
||||
- (nullable instancetype)initWithCookieJar:(NonnullOwnPtr<WebView::CookieJar>)cookie_jar;
|
||||
|
||||
- (nonnull TabController*)createNewTab:(Optional<URL::URL> const&)url
|
||||
fromTab:(nullable Tab*)tab
|
||||
@ -46,8 +39,6 @@
|
||||
- (void)removeTab:(nonnull TabController*)controller;
|
||||
|
||||
- (WebView::CookieJar&)cookieJar;
|
||||
- (Ladybird::WebContentOptions const&)webContentOptions;
|
||||
- (Optional<StringView> const&)webdriverContentIPCPath;
|
||||
- (Web::CSS::PreferredColorScheme)preferredColorScheme;
|
||||
- (Web::CSS::PreferredContrast)preferredContrast;
|
||||
- (Web::CSS::PreferredMotion)preferredMotion;
|
||||
|
@ -4,6 +4,7 @@
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWebView/Application.h>
|
||||
#include <LibWebView/SearchEngine.h>
|
||||
|
||||
#import <Application/ApplicationDelegate.h>
|
||||
@ -29,23 +30,15 @@
|
||||
|
||||
@interface ApplicationDelegate () <TaskManagerDelegate>
|
||||
{
|
||||
Vector<URL::URL> m_initial_urls;
|
||||
URL::URL m_new_tab_page_url;
|
||||
|
||||
// This will always be populated, but we cannot have a non-default constructible instance variable.
|
||||
OwnPtr<WebView::CookieJar> m_cookie_jar;
|
||||
|
||||
Ladybird::WebContentOptions m_web_content_options;
|
||||
Optional<StringView> m_webdriver_content_ipc_path;
|
||||
|
||||
Web::CSS::PreferredColorScheme m_preferred_color_scheme;
|
||||
Web::CSS::PreferredContrast m_preferred_contrast;
|
||||
Web::CSS::PreferredMotion m_preferred_motion;
|
||||
ByteString m_navigator_compatibility_mode;
|
||||
|
||||
WebView::SearchEngine m_search_engine;
|
||||
|
||||
BOOL m_allow_popups;
|
||||
}
|
||||
|
||||
@property (nonatomic, strong) NSMutableArray<TabController*>* managed_tabs;
|
||||
@ -68,12 +61,7 @@
|
||||
|
||||
@implementation ApplicationDelegate
|
||||
|
||||
- (instancetype)init:(Vector<URL::URL>)initial_urls
|
||||
newTabPageURL:(URL::URL)new_tab_page_url
|
||||
withCookieJar:(NonnullOwnPtr<WebView::CookieJar>)cookie_jar
|
||||
webContentOptions:(Ladybird::WebContentOptions const&)web_content_options
|
||||
webdriverContentIPCPath:(StringView)webdriver_content_ipc_path
|
||||
allowPopups:(BOOL)allow_popups
|
||||
- (instancetype)initWithCookieJar:(NonnullOwnPtr<WebView::CookieJar>)cookie_jar
|
||||
{
|
||||
if (self = [super init]) {
|
||||
[NSApp setMainMenu:[[NSMenu alloc] init]];
|
||||
@ -91,25 +79,14 @@
|
||||
|
||||
self.managed_tabs = [[NSMutableArray alloc] init];
|
||||
|
||||
m_initial_urls = move(initial_urls);
|
||||
m_new_tab_page_url = move(new_tab_page_url);
|
||||
|
||||
m_cookie_jar = move(cookie_jar);
|
||||
|
||||
m_web_content_options = web_content_options;
|
||||
|
||||
if (!webdriver_content_ipc_path.is_empty()) {
|
||||
m_webdriver_content_ipc_path = webdriver_content_ipc_path;
|
||||
}
|
||||
|
||||
m_preferred_color_scheme = Web::CSS::PreferredColorScheme::Auto;
|
||||
m_preferred_contrast = Web::CSS::PreferredContrast::Auto;
|
||||
m_preferred_motion = Web::CSS::PreferredMotion::Auto;
|
||||
m_navigator_compatibility_mode = "chrome";
|
||||
m_search_engine = WebView::default_search_engine();
|
||||
|
||||
m_allow_popups = allow_popups;
|
||||
|
||||
// Reduce the tooltip delay, as the default delay feels quite long.
|
||||
[[NSUserDefaults standardUserDefaults] setObject:@100 forKey:@"NSInitialToolTipDelay"];
|
||||
}
|
||||
@ -124,7 +101,7 @@
|
||||
activateTab:(Web::HTML::ActivateTab)activate_tab
|
||||
{
|
||||
auto* controller = [self createNewTab:activate_tab fromTab:tab];
|
||||
[controller loadURL:url.value_or(m_new_tab_page_url)];
|
||||
[controller loadURL:url.value_or(WebView::Application::chrome_options().new_tab_page_url)];
|
||||
|
||||
return controller;
|
||||
}
|
||||
@ -166,16 +143,6 @@
|
||||
return *m_cookie_jar;
|
||||
}
|
||||
|
||||
- (Ladybird::WebContentOptions const&)webContentOptions
|
||||
{
|
||||
return m_web_content_options;
|
||||
}
|
||||
|
||||
- (Optional<StringView> const&)webdriverContentIPCPath
|
||||
{
|
||||
return m_webdriver_content_ipc_path;
|
||||
}
|
||||
|
||||
- (Web::CSS::PreferredColorScheme)preferredColorScheme
|
||||
{
|
||||
return m_preferred_color_scheme;
|
||||
@ -213,7 +180,7 @@
|
||||
- (nonnull TabController*)createNewTab:(Web::HTML::ActivateTab)activate_tab
|
||||
fromTab:(nullable Tab*)tab
|
||||
{
|
||||
auto* controller = [[TabController alloc] init:!m_allow_popups];
|
||||
auto* controller = [[TabController alloc] init];
|
||||
[controller showWindow:nil];
|
||||
|
||||
if (tab) {
|
||||
@ -740,7 +707,7 @@
|
||||
{
|
||||
Tab* tab = nil;
|
||||
|
||||
for (auto const& url : m_initial_urls) {
|
||||
for (auto const& url : WebView::Application::chrome_options().urls) {
|
||||
auto activate_tab = tab == nil ? Web::HTML::ActivateTab::Yes : Web::HTML::ActivateTab::No;
|
||||
|
||||
auto* controller = [self createNewTab:url
|
||||
@ -749,8 +716,6 @@
|
||||
|
||||
tab = (Tab*)[controller window];
|
||||
}
|
||||
|
||||
m_initial_urls.clear();
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(NSNotification*)notification
|
||||
|
@ -104,7 +104,7 @@ struct HideCursor {
|
||||
// This returns device pixel ratio of the screen the window is opened in
|
||||
auto device_pixel_ratio = [[NSScreen mainScreen] backingScaleFactor];
|
||||
|
||||
m_web_view_bridge = MUST(Ladybird::WebViewBridge::create(move(screen_rects), device_pixel_ratio, [delegate webContentOptions], [delegate webdriverContentIPCPath], [delegate preferredColorScheme], [delegate preferredContrast], [delegate preferredMotion]));
|
||||
m_web_view_bridge = MUST(Ladybird::WebViewBridge::create(move(screen_rects), device_pixel_ratio, [delegate preferredColorScheme], [delegate preferredContrast], [delegate preferredMotion]));
|
||||
[self setWebViewCallbacks];
|
||||
|
||||
m_web_view_bridge->initialize_client();
|
||||
|
@ -1,16 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
|
||||
* Copyright (c) 2023-2024, Tim Flynn <trflynn89@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <Ladybird/HelperProcess.h>
|
||||
#include <Ladybird/Types.h>
|
||||
#include <Ladybird/Utilities.h>
|
||||
#include <LibGfx/Font/FontDatabase.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <LibIPC/File.h>
|
||||
#include <LibWeb/Crypto/Crypto.h>
|
||||
#include <LibWebView/Application.h>
|
||||
#include <UI/LadybirdWebViewBridge.h>
|
||||
|
||||
#import <UI/Palette.h>
|
||||
@ -23,15 +23,13 @@ static T scale_for_device(T size, float device_pixel_ratio)
|
||||
return size.template to_type<float>().scaled(device_pixel_ratio).template to_type<int>();
|
||||
}
|
||||
|
||||
ErrorOr<NonnullOwnPtr<WebViewBridge>> WebViewBridge::create(Vector<Web::DevicePixelRect> screen_rects, float device_pixel_ratio, WebContentOptions const& web_content_options, Optional<StringView> webdriver_content_ipc_path, Web::CSS::PreferredColorScheme preferred_color_scheme, Web::CSS::PreferredContrast preferred_contrast, Web::CSS::PreferredMotion preferred_motion)
|
||||
ErrorOr<NonnullOwnPtr<WebViewBridge>> WebViewBridge::create(Vector<Web::DevicePixelRect> screen_rects, float device_pixel_ratio, Web::CSS::PreferredColorScheme preferred_color_scheme, Web::CSS::PreferredContrast preferred_contrast, Web::CSS::PreferredMotion preferred_motion)
|
||||
{
|
||||
return adopt_nonnull_own_or_enomem(new (nothrow) WebViewBridge(move(screen_rects), device_pixel_ratio, web_content_options, move(webdriver_content_ipc_path), preferred_color_scheme, preferred_contrast, preferred_motion));
|
||||
return adopt_nonnull_own_or_enomem(new (nothrow) WebViewBridge(move(screen_rects), device_pixel_ratio, preferred_color_scheme, preferred_contrast, preferred_motion));
|
||||
}
|
||||
|
||||
WebViewBridge::WebViewBridge(Vector<Web::DevicePixelRect> screen_rects, float device_pixel_ratio, WebContentOptions const& web_content_options, Optional<StringView> webdriver_content_ipc_path, Web::CSS::PreferredColorScheme preferred_color_scheme, Web::CSS::PreferredContrast preferred_contrast, Web::CSS::PreferredMotion preferred_motion)
|
||||
WebViewBridge::WebViewBridge(Vector<Web::DevicePixelRect> screen_rects, float device_pixel_ratio, Web::CSS::PreferredColorScheme preferred_color_scheme, Web::CSS::PreferredContrast preferred_contrast, Web::CSS::PreferredMotion preferred_motion)
|
||||
: m_screen_rects(move(screen_rects))
|
||||
, m_web_content_options(web_content_options)
|
||||
, m_webdriver_content_ipc_path(move(webdriver_content_ipc_path))
|
||||
, m_preferred_color_scheme(preferred_color_scheme)
|
||||
, m_preferred_contrast(preferred_contrast)
|
||||
, m_preferred_motion(preferred_motion)
|
||||
@ -168,8 +166,8 @@ void WebViewBridge::initialize_client(CreateNewClient)
|
||||
client().async_update_screen_rects(m_client_state.page_index, m_screen_rects, 0);
|
||||
}
|
||||
|
||||
if (m_webdriver_content_ipc_path.has_value()) {
|
||||
client().async_connect_to_webdriver(m_client_state.page_index, *m_webdriver_content_ipc_path);
|
||||
if (auto const& webdriver_content_ipc_path = WebView::Application::chrome_options().webdriver_content_ipc_path; webdriver_content_ipc_path.has_value()) {
|
||||
client().async_connect_to_webdriver(m_client_state.page_index, *webdriver_content_ipc_path);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
|
||||
* Copyright (c) 2023-2024, Tim Flynn <trflynn89@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
@ -7,7 +7,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <AK/Vector.h>
|
||||
#include <Ladybird/Types.h>
|
||||
#include <LibGfx/Point.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <LibGfx/Size.h>
|
||||
@ -22,13 +21,11 @@ namespace Ladybird {
|
||||
|
||||
class WebViewBridge final : public WebView::ViewImplementation {
|
||||
public:
|
||||
static ErrorOr<NonnullOwnPtr<WebViewBridge>> create(Vector<Web::DevicePixelRect> screen_rects, float device_pixel_ratio, WebContentOptions const&, Optional<StringView> webdriver_content_ipc_path, Web::CSS::PreferredColorScheme, Web::CSS::PreferredContrast, Web::CSS::PreferredMotion);
|
||||
static ErrorOr<NonnullOwnPtr<WebViewBridge>> create(Vector<Web::DevicePixelRect> screen_rects, float device_pixel_ratio, Web::CSS::PreferredColorScheme, Web::CSS::PreferredContrast, Web::CSS::PreferredMotion);
|
||||
virtual ~WebViewBridge() override;
|
||||
|
||||
virtual void initialize_client(CreateNewClient = CreateNewClient::Yes) override;
|
||||
|
||||
WebContentOptions const& web_content_options() const { return m_web_content_options; }
|
||||
|
||||
float device_pixel_ratio() const { return m_device_pixel_ratio; }
|
||||
void set_device_pixel_ratio(float device_pixel_ratio);
|
||||
float inverse_device_pixel_ratio() const { return 1.0f / m_device_pixel_ratio; }
|
||||
@ -59,7 +56,7 @@ public:
|
||||
Function<void()> on_zoom_level_changed;
|
||||
|
||||
private:
|
||||
WebViewBridge(Vector<Web::DevicePixelRect> screen_rects, float device_pixel_ratio, WebContentOptions const&, Optional<StringView> webdriver_content_ipc_path, Web::CSS::PreferredColorScheme, Web::CSS::PreferredContrast, Web::CSS::PreferredMotion);
|
||||
WebViewBridge(Vector<Web::DevicePixelRect> screen_rects, float device_pixel_ratio, Web::CSS::PreferredColorScheme, Web::CSS::PreferredContrast, Web::CSS::PreferredMotion);
|
||||
|
||||
virtual void update_zoom() override;
|
||||
virtual Web::DevicePixelSize viewport_size() const override;
|
||||
@ -69,9 +66,6 @@ private:
|
||||
Vector<Web::DevicePixelRect> m_screen_rects;
|
||||
Gfx::IntSize m_viewport_size;
|
||||
|
||||
WebContentOptions m_web_content_options;
|
||||
Optional<StringView> m_webdriver_content_ipc_path;
|
||||
|
||||
Web::CSS::PreferredColorScheme m_preferred_color_scheme { Web::CSS::PreferredColorScheme::Auto };
|
||||
Web::CSS::PreferredContrast m_preferred_contrast { Web::CSS::PreferredContrast::Auto };
|
||||
Web::CSS::PreferredMotion m_preferred_motion { Web::CSS::PreferredMotion::Auto };
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
|
||||
* Copyright (c) 2023-2024, Tim Flynn <trflynn89@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
@ -22,7 +22,7 @@ struct TabSettings {
|
||||
|
||||
@interface TabController : NSWindowController <NSWindowDelegate>
|
||||
|
||||
- (instancetype)init:(BOOL)block_popups;
|
||||
- (instancetype)init;
|
||||
|
||||
- (void)loadURL:(URL::URL const&)url;
|
||||
- (void)loadHTML:(StringView)html url:(URL::URL const&)url;
|
||||
|
@ -1,10 +1,11 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
|
||||
* Copyright (c) 2023-2024, Tim Flynn <trflynn89@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/Loader/UserAgent.h>
|
||||
#include <LibWebView/Application.h>
|
||||
#include <LibWebView/SearchEngine.h>
|
||||
#include <LibWebView/URL.h>
|
||||
#include <LibWebView/UserAgent.h>
|
||||
@ -82,7 +83,7 @@ static NSString* const TOOLBAR_TAB_OVERVIEW_IDENTIFIER = @"ToolbarTabOverviewIde
|
||||
@synthesize new_tab_toolbar_item = _new_tab_toolbar_item;
|
||||
@synthesize tab_overview_toolbar_item = _tab_overview_toolbar_item;
|
||||
|
||||
- (instancetype)init:(BOOL)block_popups
|
||||
- (instancetype)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
self.toolbar = [[NSToolbar alloc] initWithIdentifier:TOOLBAR_IDENTIFIER];
|
||||
@ -91,7 +92,7 @@ static NSString* const TOOLBAR_TAB_OVERVIEW_IDENTIFIER = @"ToolbarTabOverviewIde
|
||||
[self.toolbar setAllowsUserCustomization:NO];
|
||||
[self.toolbar setSizeMode:NSToolbarSizeModeRegular];
|
||||
|
||||
m_settings = { .block_popups = block_popups };
|
||||
m_settings = { .block_popups = WebView::Application::chrome_options().allow_popups == WebView::AllowPopups::Yes ? NO : YES };
|
||||
m_can_navigate_back = false;
|
||||
m_can_navigate_forward = false;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
|
||||
* Copyright (c) 2023-2024, Tim Flynn <trflynn89@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
@ -7,9 +7,7 @@
|
||||
#include <AK/Enumerate.h>
|
||||
#include <Ladybird/DefaultSettings.h>
|
||||
#include <Ladybird/MachPortServer.h>
|
||||
#include <Ladybird/Types.h>
|
||||
#include <Ladybird/Utilities.h>
|
||||
#include <LibCore/ArgsParser.h>
|
||||
#include <LibGfx/Font/FontDatabase.h>
|
||||
#include <LibMain/Main.h>
|
||||
#include <LibWebView/Application.h>
|
||||
@ -30,33 +28,10 @@
|
||||
# error "This project requires ARC"
|
||||
#endif
|
||||
|
||||
static Vector<URL::URL> sanitize_urls(Vector<ByteString> const& raw_urls)
|
||||
{
|
||||
Vector<URL::URL> sanitized_urls;
|
||||
for (auto const& raw_url : raw_urls) {
|
||||
if (auto url = WebView::sanitize_url(raw_url); url.has_value())
|
||||
sanitized_urls.append(url.release_value());
|
||||
}
|
||||
|
||||
if (sanitized_urls.is_empty()) {
|
||||
URL::URL new_tab_page_url = Browser::default_new_tab_url;
|
||||
sanitized_urls.append(move(new_tab_page_url));
|
||||
}
|
||||
|
||||
return sanitized_urls;
|
||||
}
|
||||
|
||||
enum class NewWindow {
|
||||
No,
|
||||
Yes,
|
||||
};
|
||||
|
||||
static void open_urls_from_client(Vector<ByteString> const& raw_urls, NewWindow new_window)
|
||||
static void open_urls_from_client(Vector<URL::URL> const& urls, WebView::NewWindow new_window)
|
||||
{
|
||||
ApplicationDelegate* delegate = [NSApp delegate];
|
||||
Tab* tab = new_window == NewWindow::Yes ? nil : [delegate activeTab];
|
||||
|
||||
auto urls = sanitize_urls(raw_urls);
|
||||
Tab* tab = new_window == WebView::NewWindow::Yes ? nil : [delegate activeTab];
|
||||
|
||||
for (auto [i, url] : enumerate(urls)) {
|
||||
auto activate_tab = i == 0 ? Web::HTML::ActivateTab::Yes : Web::HTML::ActivateTab::No;
|
||||
@ -76,51 +51,33 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||
Application* application = [Application sharedApplication];
|
||||
|
||||
Core::EventLoopManager::install(*new Ladybird::CFEventLoopManager);
|
||||
WebView::Application web_view_app(arguments.argc, arguments.argv);
|
||||
[application setupWebViewApplication:arguments newTabPageURL:Browser::default_new_tab_url];
|
||||
|
||||
platform_init();
|
||||
|
||||
Vector<ByteString> raw_urls;
|
||||
Vector<ByteString> certificates;
|
||||
StringView webdriver_content_ipc_path;
|
||||
bool debug_web_content = false;
|
||||
bool log_all_js_exceptions = false;
|
||||
bool enable_http_cache = false;
|
||||
bool new_window = false;
|
||||
bool force_new_process = false;
|
||||
bool allow_popups = false;
|
||||
|
||||
Core::ArgsParser args_parser;
|
||||
args_parser.set_general_help("The Ladybird web browser");
|
||||
args_parser.add_positional_argument(raw_urls, "URLs to open", "url", Core::ArgsParser::Required::No);
|
||||
args_parser.add_option(webdriver_content_ipc_path, "Path to WebDriver IPC for WebContent", "webdriver-content-path", 0, "path", Core::ArgsParser::OptionHideMode::CommandLineAndMarkdown);
|
||||
args_parser.add_option(debug_web_content, "Wait for debugger to attach to WebContent", "debug-web-content");
|
||||
args_parser.add_option(certificates, "Path to a certificate file", "certificate", 'C', "certificate");
|
||||
args_parser.add_option(log_all_js_exceptions, "Log all JavaScript exceptions", "log-all-js-exceptions");
|
||||
args_parser.add_option(enable_http_cache, "Enable HTTP cache", "enable-http-cache");
|
||||
args_parser.add_option(new_window, "Force opening in a new window", "new-window", 'n');
|
||||
args_parser.add_option(force_new_process, "Force creation of new browser/chrome process", "force-new-process");
|
||||
args_parser.add_option(allow_popups, "Disable popup blocking by default", "allow-popups");
|
||||
args_parser.parse(arguments);
|
||||
|
||||
auto chrome_process = TRY(WebView::ChromeProcess::create());
|
||||
if (!force_new_process && TRY(chrome_process.connect(raw_urls, new_window)) == WebView::ChromeProcess::ProcessDisposition::ExitProcess) {
|
||||
outln("Opening in existing process");
|
||||
return 0;
|
||||
|
||||
if (auto const& chrome_options = WebView::Application::chrome_options(); chrome_options.force_new_process == WebView::ForceNewProcess::No) {
|
||||
auto disposition = TRY(chrome_process.connect(chrome_options.raw_urls, chrome_options.new_window));
|
||||
|
||||
if (disposition == WebView::ChromeProcess::ProcessDisposition::ExitProcess) {
|
||||
outln("Opening in existing process");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
chrome_process.on_new_tab = [&](auto const& raw_urls) {
|
||||
open_urls_from_client(raw_urls, NewWindow::No);
|
||||
open_urls_from_client(raw_urls, WebView::NewWindow::No);
|
||||
};
|
||||
|
||||
chrome_process.on_new_window = [&](auto const& raw_urls) {
|
||||
open_urls_from_client(raw_urls, NewWindow::Yes);
|
||||
open_urls_from_client(raw_urls, WebView::NewWindow::Yes);
|
||||
};
|
||||
|
||||
auto mach_port_server = make<Ladybird::MachPortServer>();
|
||||
set_mach_server_name(mach_port_server->server_port_name());
|
||||
mach_port_server->on_receive_child_mach_port = [&web_view_app](auto pid, auto port) {
|
||||
web_view_app.set_process_mach_port(pid, move(port));
|
||||
mach_port_server->on_receive_child_mach_port = [&](auto pid, auto port) {
|
||||
WebView::Application::the().set_process_mach_port(pid, move(port));
|
||||
};
|
||||
mach_port_server->on_receive_backing_stores = [](Ladybird::MachPortServer::BackingStoresMessage message) {
|
||||
if (auto view = WebView::WebContentClient::view_for_pid_and_page_id(message.pid, message.page_id); view.has_value())
|
||||
@ -131,28 +88,12 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||
auto cookie_jar = TRY(WebView::CookieJar::create(*database));
|
||||
|
||||
// FIXME: Create an abstraction to re-spawn the RequestServer and re-hook up its client hooks to each tab on crash
|
||||
TRY([application launchRequestServer:certificates]);
|
||||
TRY([application launchRequestServer]);
|
||||
|
||||
TRY([application launchImageDecoder]);
|
||||
|
||||
StringBuilder command_line_builder;
|
||||
command_line_builder.join(' ', arguments.strings);
|
||||
Ladybird::WebContentOptions web_content_options {
|
||||
.command_line = MUST(command_line_builder.to_string()),
|
||||
.executable_path = MUST(String::from_byte_string(MUST(Core::System::current_executable_path()))),
|
||||
.wait_for_debugger = debug_web_content ? Ladybird::WaitForDebugger::Yes : Ladybird::WaitForDebugger::No,
|
||||
.log_all_js_exceptions = log_all_js_exceptions ? Ladybird::LogAllJSExceptions::Yes : Ladybird::LogAllJSExceptions::No,
|
||||
.enable_http_cache = enable_http_cache ? Ladybird::EnableHTTPCache::Yes : Ladybird::EnableHTTPCache::No,
|
||||
};
|
||||
|
||||
auto* delegate = [[ApplicationDelegate alloc] init:sanitize_urls(raw_urls)
|
||||
newTabPageURL:URL::URL { Browser::default_new_tab_url }
|
||||
withCookieJar:move(cookie_jar)
|
||||
webContentOptions:web_content_options
|
||||
webdriverContentIPCPath:webdriver_content_ipc_path
|
||||
allowPopups:allow_popups];
|
||||
|
||||
auto* delegate = [[ApplicationDelegate alloc] initWithCookieJar:move(cookie_jar)];
|
||||
[NSApp setDelegate:delegate];
|
||||
|
||||
return web_view_app.exec();
|
||||
return WebView::Application::the().execute();
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ set(LADYBIRD_SOURCES
|
||||
)
|
||||
set(LADYBIRD_HEADERS
|
||||
HelperProcess.h
|
||||
Types.h
|
||||
Utilities.h
|
||||
)
|
||||
|
||||
|
@ -15,10 +15,10 @@ static ErrorOr<NonnullRefPtr<ClientType>> launch_server_process(
|
||||
StringView server_name,
|
||||
ReadonlySpan<ByteString> candidate_server_paths,
|
||||
Vector<ByteString> arguments,
|
||||
Ladybird::EnableCallgrindProfiling enable_callgrind_profiling,
|
||||
WebView::EnableCallgrindProfiling enable_callgrind_profiling,
|
||||
ClientArguments&&... client_arguments)
|
||||
{
|
||||
if (enable_callgrind_profiling == Ladybird::EnableCallgrindProfiling::Yes) {
|
||||
if (enable_callgrind_profiling == WebView::EnableCallgrindProfiling::Yes) {
|
||||
arguments.prepend({
|
||||
"--tool=callgrind"sv,
|
||||
"--instr-atstart=no"sv,
|
||||
@ -29,7 +29,7 @@ static ErrorOr<NonnullRefPtr<ClientType>> launch_server_process(
|
||||
for (auto [i, path] : enumerate(candidate_server_paths)) {
|
||||
Core::ProcessSpawnOptions options { .name = server_name, .arguments = arguments };
|
||||
|
||||
if (enable_callgrind_profiling == Ladybird::EnableCallgrindProfiling::Yes) {
|
||||
if (enable_callgrind_profiling == WebView::EnableCallgrindProfiling::Yes) {
|
||||
options.executable = "valgrind"sv;
|
||||
options.search_for_executable_in_path = true;
|
||||
arguments[2] = path;
|
||||
@ -47,7 +47,7 @@ static ErrorOr<NonnullRefPtr<ClientType>> launch_server_process(
|
||||
|
||||
WebView::Application::the().add_child_process(WebView::Process { WebView::process_type_from_name(server_name), process.client, move(process.process) });
|
||||
|
||||
if (enable_callgrind_profiling == Ladybird::EnableCallgrindProfiling::Yes) {
|
||||
if (enable_callgrind_profiling == WebView::EnableCallgrindProfiling::Yes) {
|
||||
dbgln();
|
||||
dbgln("\033[1;45mLaunched {} process under callgrind!\033[0m", server_name);
|
||||
dbgln("\033[100mRun `\033[4mcallgrind_control -i on\033[24m` to start instrumentation and `\033[4mcallgrind_control -i off\033[24m` stop it again.\033[0m");
|
||||
@ -69,10 +69,11 @@ static ErrorOr<NonnullRefPtr<ClientType>> launch_server_process(
|
||||
ErrorOr<NonnullRefPtr<WebView::WebContentClient>> launch_web_content_process(
|
||||
WebView::ViewImplementation& view,
|
||||
ReadonlySpan<ByteString> candidate_web_content_paths,
|
||||
Ladybird::WebContentOptions const& web_content_options,
|
||||
IPC::File image_decoder_socket,
|
||||
Optional<IPC::File> request_server_socket)
|
||||
{
|
||||
auto const& web_content_options = WebView::Application::web_content_options();
|
||||
|
||||
Vector<ByteString> arguments {
|
||||
"--command-line"sv,
|
||||
web_content_options.command_line.to_byte_string(),
|
||||
@ -84,19 +85,19 @@ ErrorOr<NonnullRefPtr<WebView::WebContentClient>> launch_web_content_process(
|
||||
arguments.append("--config-path"sv);
|
||||
arguments.append(web_content_options.config_path.value());
|
||||
}
|
||||
if (web_content_options.is_layout_test_mode == Ladybird::IsLayoutTestMode::Yes)
|
||||
if (web_content_options.is_layout_test_mode == WebView::IsLayoutTestMode::Yes)
|
||||
arguments.append("--layout-test-mode"sv);
|
||||
if (web_content_options.use_lagom_networking == Ladybird::UseLagomNetworking::Yes)
|
||||
if (web_content_options.use_lagom_networking == WebView::UseLagomNetworking::Yes)
|
||||
arguments.append("--use-lagom-networking"sv);
|
||||
if (web_content_options.wait_for_debugger == Ladybird::WaitForDebugger::Yes)
|
||||
if (web_content_options.wait_for_debugger == WebView::WaitForDebugger::Yes)
|
||||
arguments.append("--wait-for-debugger"sv);
|
||||
if (web_content_options.log_all_js_exceptions == Ladybird::LogAllJSExceptions::Yes)
|
||||
if (web_content_options.log_all_js_exceptions == WebView::LogAllJSExceptions::Yes)
|
||||
arguments.append("--log-all-js-exceptions"sv);
|
||||
if (web_content_options.enable_idl_tracing == Ladybird::EnableIDLTracing::Yes)
|
||||
if (web_content_options.enable_idl_tracing == WebView::EnableIDLTracing::Yes)
|
||||
arguments.append("--enable-idl-tracing"sv);
|
||||
if (web_content_options.enable_http_cache == Ladybird::EnableHTTPCache::Yes)
|
||||
if (web_content_options.enable_http_cache == WebView::EnableHTTPCache::Yes)
|
||||
arguments.append("--enable-http-cache"sv);
|
||||
if (web_content_options.expose_internals_object == Ladybird::ExposeInternalsObject::Yes)
|
||||
if (web_content_options.expose_internals_object == WebView::ExposeInternalsObject::Yes)
|
||||
arguments.append("--expose-internals-object"sv);
|
||||
if (auto server = mach_server_name(); server.has_value()) {
|
||||
arguments.append("--mach-server-name"sv);
|
||||
@ -121,7 +122,7 @@ ErrorOr<NonnullRefPtr<ImageDecoderClient::Client>> launch_image_decoder_process(
|
||||
arguments.append(server.value());
|
||||
}
|
||||
|
||||
return launch_server_process<ImageDecoderClient::Client>("ImageDecoder"sv, candidate_image_decoder_paths, arguments, Ladybird::EnableCallgrindProfiling::No);
|
||||
return launch_server_process<ImageDecoderClient::Client>("ImageDecoder"sv, candidate_image_decoder_paths, arguments, WebView::EnableCallgrindProfiling::No);
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<Web::HTML::WebWorkerClient>> launch_web_worker_process(ReadonlySpan<ByteString> candidate_web_worker_paths, RefPtr<Protocol::RequestClient> request_client)
|
||||
@ -132,13 +133,13 @@ ErrorOr<NonnullRefPtr<Web::HTML::WebWorkerClient>> launch_web_worker_process(Rea
|
||||
arguments.append("--request-server-socket"sv);
|
||||
arguments.append(ByteString::number(socket.fd()));
|
||||
arguments.append("--use-lagom-networking"sv);
|
||||
return launch_server_process<Web::HTML::WebWorkerClient>("WebWorker"sv, candidate_web_worker_paths, move(arguments), Ladybird::EnableCallgrindProfiling::No);
|
||||
return launch_server_process<Web::HTML::WebWorkerClient>("WebWorker"sv, candidate_web_worker_paths, move(arguments), WebView::EnableCallgrindProfiling::No);
|
||||
}
|
||||
|
||||
return launch_server_process<Web::HTML::WebWorkerClient>("WebWorker"sv, candidate_web_worker_paths, move(arguments), Ladybird::EnableCallgrindProfiling::No);
|
||||
return launch_server_process<Web::HTML::WebWorkerClient>("WebWorker"sv, candidate_web_worker_paths, move(arguments), WebView::EnableCallgrindProfiling::No);
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<Protocol::RequestClient>> launch_request_server_process(ReadonlySpan<ByteString> candidate_request_server_paths, StringView serenity_resource_root, Vector<ByteString> const& certificates)
|
||||
ErrorOr<NonnullRefPtr<Protocol::RequestClient>> launch_request_server_process(ReadonlySpan<ByteString> candidate_request_server_paths, StringView serenity_resource_root)
|
||||
{
|
||||
Vector<ByteString> arguments;
|
||||
|
||||
@ -147,7 +148,7 @@ ErrorOr<NonnullRefPtr<Protocol::RequestClient>> launch_request_server_process(Re
|
||||
arguments.append(serenity_resource_root);
|
||||
}
|
||||
|
||||
for (auto const& certificate : certificates)
|
||||
for (auto const& certificate : WebView::Application::chrome_options().certificates)
|
||||
arguments.append(ByteString::formatted("--certificate={}", certificate));
|
||||
|
||||
if (auto server = mach_server_name(); server.has_value()) {
|
||||
@ -155,7 +156,7 @@ ErrorOr<NonnullRefPtr<Protocol::RequestClient>> launch_request_server_process(Re
|
||||
arguments.append(server.value());
|
||||
}
|
||||
|
||||
return launch_server_process<Protocol::RequestClient>("RequestServer"sv, candidate_request_server_paths, move(arguments), Ladybird::EnableCallgrindProfiling::No);
|
||||
return launch_server_process<Protocol::RequestClient>("RequestServer"sv, candidate_request_server_paths, move(arguments), WebView::EnableCallgrindProfiling::No);
|
||||
}
|
||||
|
||||
ErrorOr<IPC::File> connect_new_request_server_client(Protocol::RequestClient& client)
|
||||
|
@ -6,7 +6,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Types.h"
|
||||
#include <AK/Error.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/Span.h>
|
||||
@ -20,13 +19,12 @@
|
||||
ErrorOr<NonnullRefPtr<WebView::WebContentClient>> launch_web_content_process(
|
||||
WebView::ViewImplementation& view,
|
||||
ReadonlySpan<ByteString> candidate_web_content_paths,
|
||||
Ladybird::WebContentOptions const&,
|
||||
IPC::File image_decoder_socket,
|
||||
Optional<IPC::File> request_server_socket = {});
|
||||
|
||||
ErrorOr<NonnullRefPtr<ImageDecoderClient::Client>> launch_image_decoder_process(ReadonlySpan<ByteString> candidate_image_decoder_paths);
|
||||
ErrorOr<NonnullRefPtr<Web::HTML::WebWorkerClient>> launch_web_worker_process(ReadonlySpan<ByteString> candidate_web_worker_paths, RefPtr<Protocol::RequestClient>);
|
||||
ErrorOr<NonnullRefPtr<Protocol::RequestClient>> launch_request_server_process(ReadonlySpan<ByteString> candidate_request_server_paths, StringView serenity_resource_root, Vector<ByteString> const& certificates);
|
||||
ErrorOr<NonnullRefPtr<Protocol::RequestClient>> launch_request_server_process(ReadonlySpan<ByteString> candidate_request_server_paths, StringView serenity_resource_root);
|
||||
|
||||
ErrorOr<IPC::File> connect_new_request_server_client(Protocol::RequestClient&);
|
||||
ErrorOr<IPC::File> connect_new_image_decoder_client(ImageDecoderClient::Client&);
|
||||
|
@ -4,21 +4,34 @@
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "Application.h"
|
||||
#include "StringUtils.h"
|
||||
#include "TaskManagerWindow.h"
|
||||
#include <Ladybird/HelperProcess.h>
|
||||
#include <Ladybird/Qt/Application.h>
|
||||
#include <Ladybird/Qt/Settings.h>
|
||||
#include <Ladybird/Qt/StringUtils.h>
|
||||
#include <Ladybird/Qt/TaskManagerWindow.h>
|
||||
#include <Ladybird/Utilities.h>
|
||||
#include <LibCore/ArgsParser.h>
|
||||
#include <LibWebView/URL.h>
|
||||
#include <QFileOpenEvent>
|
||||
|
||||
namespace Ladybird {
|
||||
|
||||
Application::Application(int& argc, char** argv)
|
||||
: QApplication(argc, argv)
|
||||
Application::Application(Badge<WebView::Application>, Main::Arguments& arguments)
|
||||
: QApplication(arguments.argc, arguments.argv)
|
||||
{
|
||||
}
|
||||
|
||||
void Application::create_platform_arguments(Core::ArgsParser& args_parser)
|
||||
{
|
||||
args_parser.add_option(m_enable_qt_networking, "Enable Qt as the backend networking service", "enable-qt-networking");
|
||||
}
|
||||
|
||||
void Application::create_platform_options(WebView::ChromeOptions&, WebView::WebContentOptions& web_content_options)
|
||||
{
|
||||
web_content_options.config_path = Settings::the()->directory();
|
||||
web_content_options.use_lagom_networking = m_enable_qt_networking ? WebView::UseLagomNetworking::No : WebView::UseLagomNetworking::Yes;
|
||||
}
|
||||
|
||||
Application::~Application()
|
||||
{
|
||||
close_task_manager_window();
|
||||
@ -77,10 +90,10 @@ ErrorOr<void> Application::initialize_image_decoder()
|
||||
return {};
|
||||
}
|
||||
|
||||
void Application::show_task_manager_window(WebContentOptions const& web_content_options)
|
||||
void Application::show_task_manager_window()
|
||||
{
|
||||
if (!m_task_manager_window) {
|
||||
m_task_manager_window = new TaskManagerWindow(nullptr, web_content_options);
|
||||
m_task_manager_window = new TaskManagerWindow(nullptr);
|
||||
}
|
||||
m_task_manager_window->show();
|
||||
m_task_manager_window->activateWindow();
|
||||
@ -96,9 +109,9 @@ void Application::close_task_manager_window()
|
||||
}
|
||||
}
|
||||
|
||||
BrowserWindow& Application::new_window(Vector<URL::URL> const& initial_urls, WebView::CookieJar& cookie_jar, WebContentOptions const& web_content_options, StringView webdriver_content_ipc_path, bool allow_popups, BrowserWindow::IsPopupWindow is_popup_window, Tab* parent_tab, Optional<u64> page_index)
|
||||
BrowserWindow& Application::new_window(Vector<URL::URL> const& initial_urls, WebView::CookieJar& cookie_jar, BrowserWindow::IsPopupWindow is_popup_window, Tab* parent_tab, Optional<u64> page_index)
|
||||
{
|
||||
auto* window = new BrowserWindow(initial_urls, cookie_jar, web_content_options, webdriver_content_ipc_path, allow_popups, is_popup_window, parent_tab, move(page_index));
|
||||
auto* window = new BrowserWindow(initial_urls, cookie_jar, is_popup_window, parent_tab, move(page_index));
|
||||
set_active_window(*window);
|
||||
window->show();
|
||||
if (initial_urls.is_empty()) {
|
||||
|
@ -12,15 +12,18 @@
|
||||
#include <LibImageDecoderClient/Client.h>
|
||||
#include <LibProtocol/RequestClient.h>
|
||||
#include <LibURL/URL.h>
|
||||
#include <LibWebView/Application.h>
|
||||
#include <QApplication>
|
||||
|
||||
namespace Ladybird {
|
||||
|
||||
class Application : public QApplication {
|
||||
class Application
|
||||
: public QApplication
|
||||
, public WebView::Application {
|
||||
Q_OBJECT
|
||||
WEB_VIEW_APPLICATION(Application)
|
||||
|
||||
public:
|
||||
Application(int& argc, char** argv);
|
||||
virtual ~Application() override;
|
||||
|
||||
virtual bool event(QEvent* event) override;
|
||||
@ -31,15 +34,20 @@ public:
|
||||
NonnullRefPtr<ImageDecoderClient::Client> image_decoder_client() const { return *m_image_decoder_client; }
|
||||
ErrorOr<void> initialize_image_decoder();
|
||||
|
||||
BrowserWindow& new_window(Vector<URL::URL> const& initial_urls, WebView::CookieJar&, WebContentOptions const&, StringView webdriver_content_ipc_path, bool allow_popups, BrowserWindow::IsPopupWindow is_popup_window = BrowserWindow::IsPopupWindow::No, Tab* parent_tab = nullptr, Optional<u64> page_index = {});
|
||||
BrowserWindow& new_window(Vector<URL::URL> const& initial_urls, WebView::CookieJar&, BrowserWindow::IsPopupWindow is_popup_window = BrowserWindow::IsPopupWindow::No, Tab* parent_tab = nullptr, Optional<u64> page_index = {});
|
||||
|
||||
void show_task_manager_window(WebContentOptions const&);
|
||||
void show_task_manager_window();
|
||||
void close_task_manager_window();
|
||||
|
||||
BrowserWindow& active_window() { return *m_active_window; }
|
||||
void set_active_window(BrowserWindow& w) { m_active_window = &w; }
|
||||
|
||||
private:
|
||||
virtual void create_platform_arguments(Core::ArgsParser&) override;
|
||||
virtual void create_platform_options(WebView::ChromeOptions&, WebView::WebContentOptions&) override;
|
||||
|
||||
bool m_enable_qt_networking { false };
|
||||
|
||||
TaskManagerWindow* m_task_manager_window { nullptr };
|
||||
BrowserWindow* m_active_window { nullptr };
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <LibWeb/CSS/PreferredContrast.h>
|
||||
#include <LibWeb/CSS/PreferredMotion.h>
|
||||
#include <LibWeb/Loader/UserAgent.h>
|
||||
#include <LibWebView/Application.h>
|
||||
#include <LibWebView/CookieJar.h>
|
||||
#include <LibWebView/UserAgent.h>
|
||||
#include <QAction>
|
||||
@ -71,13 +72,10 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
BrowserWindow::BrowserWindow(Vector<URL::URL> const& initial_urls, WebView::CookieJar& cookie_jar, WebContentOptions const& web_content_options, StringView webdriver_content_ipc_path, bool allow_popups, IsPopupWindow is_popup_window, Tab* parent_tab, Optional<u64> page_index)
|
||||
BrowserWindow::BrowserWindow(Vector<URL::URL> const& initial_urls, WebView::CookieJar& cookie_jar, IsPopupWindow is_popup_window, Tab* parent_tab, Optional<u64> page_index)
|
||||
: m_tabs_container(new TabWidget(this))
|
||||
, m_new_tab_button_toolbar(new QToolBar("New Tab", m_tabs_container))
|
||||
, m_cookie_jar(cookie_jar)
|
||||
, m_web_content_options(web_content_options)
|
||||
, m_webdriver_content_ipc_path(webdriver_content_ipc_path)
|
||||
, m_allow_popups(allow_popups)
|
||||
, m_is_popup_window(is_popup_window)
|
||||
{
|
||||
setWindowIcon(app_icon());
|
||||
@ -365,7 +363,7 @@ BrowserWindow::BrowserWindow(Vector<URL::URL> const& initial_urls, WebView::Cook
|
||||
task_manager_action->setShortcuts({ QKeySequence("Ctrl+Shift+M") });
|
||||
inspect_menu->addAction(task_manager_action);
|
||||
QObject::connect(task_manager_action, &QAction::triggered, this, [&] {
|
||||
static_cast<Ladybird::Application*>(QApplication::instance())->show_task_manager_window(m_web_content_options);
|
||||
static_cast<Ladybird::Application*>(QApplication::instance())->show_task_manager_window();
|
||||
});
|
||||
|
||||
auto* debug_menu = m_hamburger_menu->addMenu("&Debug");
|
||||
@ -556,7 +554,7 @@ BrowserWindow::BrowserWindow(Vector<URL::URL> const& initial_urls, WebView::Cook
|
||||
|
||||
m_block_pop_ups_action = new QAction("Block Pop-ups", this);
|
||||
m_block_pop_ups_action->setCheckable(true);
|
||||
m_block_pop_ups_action->setChecked(!allow_popups);
|
||||
m_block_pop_ups_action->setChecked(WebView::Application::chrome_options().allow_popups == WebView::AllowPopups::No);
|
||||
debug_menu->addAction(m_block_pop_ups_action);
|
||||
QObject::connect(m_block_pop_ups_action, &QAction::triggered, this, [this] {
|
||||
bool state = m_block_pop_ups_action->isChecked();
|
||||
@ -599,7 +597,7 @@ BrowserWindow::BrowserWindow(Vector<URL::URL> const& initial_urls, WebView::Cook
|
||||
tab.focus_location_editor();
|
||||
});
|
||||
QObject::connect(m_new_window_action, &QAction::triggered, this, [this] {
|
||||
(void)static_cast<Ladybird::Application*>(QApplication::instance())->new_window({}, m_cookie_jar, m_web_content_options, m_webdriver_content_ipc_path, m_allow_popups);
|
||||
(void)static_cast<Ladybird::Application*>(QApplication::instance())->new_window({}, m_cookie_jar);
|
||||
});
|
||||
QObject::connect(open_file_action, &QAction::triggered, this, &BrowserWindow::open_file);
|
||||
QObject::connect(settings_action, &QAction::triggered, this, [this] {
|
||||
@ -667,12 +665,8 @@ BrowserWindow::BrowserWindow(Vector<URL::URL> const& initial_urls, WebView::Cook
|
||||
if (parent_tab) {
|
||||
new_child_tab(Web::HTML::ActivateTab::Yes, *parent_tab, AK::move(page_index));
|
||||
} else {
|
||||
if (initial_urls.is_empty()) {
|
||||
new_tab_from_url(ak_url_from_qstring(Settings::the()->new_tab_page()), Web::HTML::ActivateTab::Yes);
|
||||
} else {
|
||||
for (size_t i = 0; i < initial_urls.size(); ++i) {
|
||||
new_tab_from_url(initial_urls[i], (i == 0) ? Web::HTML::ActivateTab::Yes : Web::HTML::ActivateTab::No);
|
||||
}
|
||||
for (size_t i = 0; i < initial_urls.size(); ++i) {
|
||||
new_tab_from_url(initial_urls[i], (i == 0) ? Web::HTML::ActivateTab::Yes : Web::HTML::ActivateTab::No);
|
||||
}
|
||||
}
|
||||
|
||||
@ -726,7 +720,7 @@ Tab& BrowserWindow::create_new_tab(Web::HTML::ActivateTab activate_tab, Tab& par
|
||||
if (!page_index.has_value())
|
||||
return create_new_tab(activate_tab);
|
||||
|
||||
auto* tab = new Tab(this, m_web_content_options, m_webdriver_content_ipc_path, parent.view().client(), page_index.value());
|
||||
auto* tab = new Tab(this, parent.view().client(), page_index.value());
|
||||
|
||||
// FIXME: Merge with other overload
|
||||
if (m_current_tab == nullptr) {
|
||||
@ -743,7 +737,7 @@ Tab& BrowserWindow::create_new_tab(Web::HTML::ActivateTab activate_tab, Tab& par
|
||||
|
||||
Tab& BrowserWindow::create_new_tab(Web::HTML::ActivateTab activate_tab)
|
||||
{
|
||||
auto* tab = new Tab(this, m_web_content_options, m_webdriver_content_ipc_path);
|
||||
auto* tab = new Tab(this);
|
||||
|
||||
if (m_current_tab == nullptr) {
|
||||
set_current_tab(tab);
|
||||
@ -775,7 +769,7 @@ void BrowserWindow::initialize_tab(Tab* tab)
|
||||
|
||||
tab->view().on_new_web_view = [this, tab](auto activate_tab, Web::HTML::WebViewHints hints, Optional<u64> page_index) {
|
||||
if (hints.popup) {
|
||||
auto& window = static_cast<Ladybird::Application*>(QApplication::instance())->new_window({}, m_cookie_jar, m_web_content_options, m_webdriver_content_ipc_path, m_allow_popups, IsPopupWindow::Yes, tab, AK::move(page_index));
|
||||
auto& window = static_cast<Ladybird::Application*>(QApplication::instance())->new_window({}, m_cookie_jar, IsPopupWindow::Yes, tab, AK::move(page_index));
|
||||
window.set_window_rect(hints.screen_x, hints.screen_y, hints.width, hints.height);
|
||||
return window.current_tab()->view().handle();
|
||||
}
|
||||
|
@ -9,7 +9,6 @@
|
||||
|
||||
#include "Tab.h"
|
||||
#include <Ladybird/Qt/FindInPageWidget.h>
|
||||
#include <Ladybird/Types.h>
|
||||
#include <LibCore/Forward.h>
|
||||
#include <LibWeb/HTML/ActivateTab.h>
|
||||
#include <LibWeb/HTML/AudioPlayState.h>
|
||||
@ -36,7 +35,7 @@ public:
|
||||
Yes,
|
||||
};
|
||||
|
||||
BrowserWindow(Vector<URL::URL> const& initial_urls, WebView::CookieJar&, WebContentOptions const&, StringView webdriver_content_ipc_path, bool allow_popups, IsPopupWindow is_popup_window = IsPopupWindow::No, Tab* parent_tab = nullptr, Optional<u64> page_index = {});
|
||||
BrowserWindow(Vector<URL::URL> const& initial_urls, WebView::CookieJar&, IsPopupWindow is_popup_window = IsPopupWindow::No, Tab* parent_tab = nullptr, Optional<u64> page_index = {});
|
||||
|
||||
WebContentView& view() const { return m_current_tab->view(); }
|
||||
|
||||
@ -215,10 +214,6 @@ private:
|
||||
|
||||
WebView::CookieJar& m_cookie_jar;
|
||||
|
||||
WebContentOptions m_web_content_options;
|
||||
StringView m_webdriver_content_ipc_path;
|
||||
|
||||
bool m_allow_popups { false };
|
||||
IsPopupWindow m_is_popup_window { IsPopupWindow::No };
|
||||
};
|
||||
|
||||
|
@ -22,7 +22,7 @@ extern bool is_using_dark_system_theme(QWidget&);
|
||||
InspectorWidget::InspectorWidget(QWidget* tab, WebContentView& content_view)
|
||||
: QWidget(tab, Qt::Window)
|
||||
{
|
||||
m_inspector_view = new WebContentView(this, content_view.web_content_options(), {});
|
||||
m_inspector_view = new WebContentView(this);
|
||||
|
||||
if (is_using_dark_system_theme(*this))
|
||||
m_inspector_view->update_palette(WebContentView::PaletteMode::Dark);
|
||||
|
@ -47,7 +47,7 @@ static QIcon default_favicon()
|
||||
return icon;
|
||||
}
|
||||
|
||||
Tab::Tab(BrowserWindow* window, WebContentOptions const& web_content_options, StringView webdriver_content_ipc_path, RefPtr<WebView::WebContentClient> parent_client, size_t page_index)
|
||||
Tab::Tab(BrowserWindow* window, RefPtr<WebView::WebContentClient> parent_client, size_t page_index)
|
||||
: QWidget(window)
|
||||
, m_window(window)
|
||||
{
|
||||
@ -55,7 +55,7 @@ Tab::Tab(BrowserWindow* window, WebContentOptions const& web_content_options, St
|
||||
m_layout->setSpacing(0);
|
||||
m_layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
m_view = new WebContentView(this, web_content_options, webdriver_content_ipc_path, parent_client, page_index);
|
||||
m_view = new WebContentView(this, parent_client, page_index);
|
||||
m_find_in_page = new FindInPageWidget(this, m_view);
|
||||
m_find_in_page->setVisible(false);
|
||||
m_toolbar = new QToolBar(this);
|
||||
|
@ -29,7 +29,7 @@ class Tab final : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Tab(BrowserWindow* window, WebContentOptions const&, StringView webdriver_content_ipc_path, RefPtr<WebView::WebContentClient> parent_client = nullptr, size_t page_index = 0);
|
||||
Tab(BrowserWindow* window, RefPtr<WebView::WebContentClient> parent_client = nullptr, size_t page_index = 0);
|
||||
virtual ~Tab() override;
|
||||
|
||||
WebContentView& view() { return *m_view; }
|
||||
|
@ -10,9 +10,9 @@
|
||||
|
||||
namespace Ladybird {
|
||||
|
||||
TaskManagerWindow::TaskManagerWindow(QWidget* parent, WebContentOptions const& web_content_options)
|
||||
TaskManagerWindow::TaskManagerWindow(QWidget* parent)
|
||||
: QWidget(parent, Qt::WindowFlags(Qt::WindowType::Window))
|
||||
, m_web_view(new WebContentView(this, web_content_options, {}))
|
||||
, m_web_view(new WebContentView(this))
|
||||
{
|
||||
setLayout(new QVBoxLayout);
|
||||
layout()->addWidget(m_web_view);
|
||||
|
@ -16,7 +16,7 @@ class TaskManagerWindow : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TaskManagerWindow(QWidget* parent, WebContentOptions const&);
|
||||
explicit TaskManagerWindow(QWidget* parent);
|
||||
|
||||
private:
|
||||
virtual void showEvent(QShowEvent*) override;
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <LibWeb/UIEvents/KeyCode.h>
|
||||
#include <LibWeb/UIEvents/MouseButton.h>
|
||||
#include <LibWeb/Worker/WebWorkerClient.h>
|
||||
#include <LibWebView/Application.h>
|
||||
#include <LibWebView/WebContentClient.h>
|
||||
#include <QApplication>
|
||||
#include <QCursor>
|
||||
@ -50,10 +51,8 @@ namespace Ladybird {
|
||||
|
||||
bool is_using_dark_system_theme(QWidget&);
|
||||
|
||||
WebContentView::WebContentView(QWidget* window, WebContentOptions const& web_content_options, StringView webdriver_content_ipc_path, RefPtr<WebView::WebContentClient> parent_client, size_t page_index)
|
||||
WebContentView::WebContentView(QWidget* window, RefPtr<WebView::WebContentClient> parent_client, size_t page_index)
|
||||
: QAbstractScrollArea(window)
|
||||
, m_web_content_options(web_content_options)
|
||||
, m_webdriver_content_ipc_path(webdriver_content_ipc_path)
|
||||
{
|
||||
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
@ -133,7 +132,7 @@ WebContentView::WebContentView(QWidget* window, WebContentOptions const& web_con
|
||||
|
||||
on_request_worker_agent = [&]() {
|
||||
RefPtr<Protocol::RequestClient> request_server_client {};
|
||||
if (m_web_content_options.use_lagom_networking == Ladybird::UseLagomNetworking::Yes)
|
||||
if (WebView::Application::web_content_options().use_lagom_networking == WebView::UseLagomNetworking::Yes)
|
||||
request_server_client = static_cast<Ladybird::Application*>(QApplication::instance())->request_server_client;
|
||||
|
||||
auto worker_client = MUST(launch_web_worker_process(MUST(get_paths_for_helper_process("WebWorker"sv)), request_server_client));
|
||||
@ -574,7 +573,7 @@ void WebContentView::initialize_client(WebView::ViewImplementation::CreateNewCli
|
||||
m_client_state = {};
|
||||
|
||||
Optional<IPC::File> request_server_socket;
|
||||
if (m_web_content_options.use_lagom_networking == UseLagomNetworking::Yes) {
|
||||
if (WebView::Application::web_content_options().use_lagom_networking == WebView::UseLagomNetworking::Yes) {
|
||||
auto& protocol = static_cast<Ladybird::Application*>(QApplication::instance())->request_server_client;
|
||||
|
||||
// FIXME: Fail to open the tab, rather than crashing the whole application if this fails
|
||||
@ -586,7 +585,7 @@ void WebContentView::initialize_client(WebView::ViewImplementation::CreateNewCli
|
||||
auto image_decoder_socket = connect_new_image_decoder_client(*image_decoder).release_value_but_fixme_should_propagate_errors();
|
||||
|
||||
auto candidate_web_content_paths = get_paths_for_helper_process("WebContent"sv).release_value_but_fixme_should_propagate_errors();
|
||||
auto new_client = launch_web_content_process(*this, candidate_web_content_paths, m_web_content_options, AK::move(image_decoder_socket), AK::move(request_server_socket)).release_value_but_fixme_should_propagate_errors();
|
||||
auto new_client = launch_web_content_process(*this, candidate_web_content_paths, AK::move(image_decoder_socket), AK::move(request_server_socket)).release_value_but_fixme_should_propagate_errors();
|
||||
|
||||
m_client_state.client = new_client;
|
||||
} else {
|
||||
@ -607,8 +606,8 @@ void WebContentView::initialize_client(WebView::ViewImplementation::CreateNewCli
|
||||
|
||||
update_screen_rects();
|
||||
|
||||
if (!m_webdriver_content_ipc_path.is_empty())
|
||||
client().async_connect_to_webdriver(m_client_state.page_index, m_webdriver_content_ipc_path);
|
||||
if (auto webdriver_content_ipc_path = WebView::Application::chrome_options().webdriver_content_ipc_path; webdriver_content_ipc_path.has_value())
|
||||
client().async_connect_to_webdriver(m_client_state.page_index, *webdriver_content_ipc_path);
|
||||
}
|
||||
|
||||
void WebContentView::update_cursor(Gfx::StandardCursor cursor)
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include <AK/Function.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <Ladybird/Types.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <LibGfx/StandardCursor.h>
|
||||
@ -46,7 +45,7 @@ class WebContentView final
|
||||
, public WebView::ViewImplementation {
|
||||
Q_OBJECT
|
||||
public:
|
||||
WebContentView(QWidget* window, WebContentOptions const&, StringView webdriver_content_ipc_path, RefPtr<WebView::WebContentClient> parent_client = nullptr, size_t page_index = 0);
|
||||
WebContentView(QWidget* window, RefPtr<WebView::WebContentClient> parent_client = nullptr, size_t page_index = 0);
|
||||
virtual ~WebContentView() override;
|
||||
|
||||
Function<String(const URL::URL&, Web::HTML::ActivateTab)> on_tab_open_request;
|
||||
@ -85,8 +84,6 @@ public:
|
||||
|
||||
QPoint map_point_to_global_position(Gfx::IntPoint) const;
|
||||
|
||||
WebContentOptions const& web_content_options() const { return m_web_content_options; }
|
||||
|
||||
signals:
|
||||
void urls_dropped(QList<QUrl> const&);
|
||||
|
||||
@ -113,9 +110,6 @@ private:
|
||||
bool m_should_show_line_box_borders { false };
|
||||
|
||||
Gfx::IntSize m_viewport_size;
|
||||
|
||||
WebContentOptions m_web_content_options;
|
||||
StringView m_webdriver_content_ipc_path;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -62,72 +62,32 @@ static ErrorOr<void> handle_attached_debugger()
|
||||
return {};
|
||||
}
|
||||
|
||||
static Vector<URL::URL> sanitize_urls(Vector<ByteString> const& raw_urls)
|
||||
{
|
||||
Vector<URL::URL> sanitized_urls;
|
||||
for (auto const& raw_url : raw_urls) {
|
||||
if (auto url = WebView::sanitize_url(raw_url); url.has_value())
|
||||
sanitized_urls.append(url.release_value());
|
||||
}
|
||||
return sanitized_urls;
|
||||
}
|
||||
|
||||
ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||
{
|
||||
AK::set_rich_debug_enabled(true);
|
||||
|
||||
Ladybird::Application app(arguments.argc, arguments.argv);
|
||||
|
||||
Core::EventLoopManager::install(*new Ladybird::EventLoopManagerQt);
|
||||
WebView::Application webview_app(arguments.argc, arguments.argv);
|
||||
static_cast<Ladybird::EventLoopImplementationQt&>(Core::EventLoop::current().impl()).set_main_loop();
|
||||
|
||||
auto app = Ladybird::Application::create(arguments, ak_url_from_qstring(Ladybird::Settings::the()->new_tab_page()));
|
||||
|
||||
static_cast<Ladybird::EventLoopImplementationQt&>(Core::EventLoop::current().impl()).set_main_loop();
|
||||
TRY(handle_attached_debugger());
|
||||
|
||||
platform_init();
|
||||
|
||||
Vector<ByteString> raw_urls;
|
||||
StringView webdriver_content_ipc_path;
|
||||
Vector<ByteString> certificates;
|
||||
bool enable_callgrind_profiling = false;
|
||||
bool disable_sql_database = false;
|
||||
bool enable_qt_networking = false;
|
||||
bool expose_internals_object = false;
|
||||
bool debug_web_content = false;
|
||||
bool log_all_js_exceptions = false;
|
||||
bool enable_idl_tracing = false;
|
||||
bool enable_http_cache = false;
|
||||
bool new_window = false;
|
||||
bool force_new_process = false;
|
||||
bool allow_popups = false;
|
||||
|
||||
Core::ArgsParser args_parser;
|
||||
args_parser.set_general_help("The Ladybird web browser :^)");
|
||||
args_parser.add_positional_argument(raw_urls, "URLs to open", "url", Core::ArgsParser::Required::No);
|
||||
args_parser.add_option(webdriver_content_ipc_path, "Path to WebDriver IPC for WebContent", "webdriver-content-path", 0, "path", Core::ArgsParser::OptionHideMode::CommandLineAndMarkdown);
|
||||
args_parser.add_option(enable_callgrind_profiling, "Enable Callgrind profiling", "enable-callgrind-profiling", 'P');
|
||||
args_parser.add_option(disable_sql_database, "Disable SQL database", "disable-sql-database");
|
||||
args_parser.add_option(enable_qt_networking, "Enable Qt as the backend networking service", "enable-qt-networking");
|
||||
args_parser.add_option(debug_web_content, "Wait for debugger to attach to WebContent", "debug-web-content");
|
||||
args_parser.add_option(certificates, "Path to a certificate file", "certificate", 'C', "certificate");
|
||||
args_parser.add_option(log_all_js_exceptions, "Log all JavaScript exceptions", "log-all-js-exceptions");
|
||||
args_parser.add_option(enable_idl_tracing, "Enable IDL tracing", "enable-idl-tracing");
|
||||
args_parser.add_option(enable_http_cache, "Enable HTTP cache", "enable-http-cache");
|
||||
args_parser.add_option(expose_internals_object, "Expose internals object", "expose-internals-object");
|
||||
args_parser.add_option(new_window, "Force opening in a new window", "new-window", 'n');
|
||||
args_parser.add_option(force_new_process, "Force creation of new browser/chrome process", "force-new-process");
|
||||
args_parser.add_option(allow_popups, "Disable popup blocking by default", "allow-popups");
|
||||
args_parser.parse(arguments);
|
||||
|
||||
auto chrome_process = TRY(WebView::ChromeProcess::create());
|
||||
if (!force_new_process && TRY(chrome_process.connect(raw_urls, new_window)) == WebView::ChromeProcess::ProcessDisposition::ExitProcess) {
|
||||
outln("Opening in existing process");
|
||||
return 0;
|
||||
|
||||
if (app->chrome_options().force_new_process == WebView::ForceNewProcess::No) {
|
||||
auto disposition = TRY(chrome_process.connect(app->chrome_options().raw_urls, app->chrome_options().new_window));
|
||||
|
||||
if (disposition == WebView::ChromeProcess::ProcessDisposition::ExitProcess) {
|
||||
outln("Opening in existing process");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
chrome_process.on_new_tab = [&](auto const& raw_urls) {
|
||||
auto& window = app.active_window();
|
||||
auto urls = sanitize_urls(raw_urls);
|
||||
chrome_process.on_new_tab = [&](auto const& urls) {
|
||||
auto& window = app->active_window();
|
||||
for (size_t i = 0; i < urls.size(); ++i) {
|
||||
window.new_tab_from_url(urls[i], (i == 0) ? Web::HTML::ActivateTab::Yes : Web::HTML::ActivateTab::No);
|
||||
}
|
||||
@ -136,16 +96,16 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||
window.raise();
|
||||
};
|
||||
|
||||
app.on_open_file = [&](auto file_url) {
|
||||
auto& window = app.active_window();
|
||||
app->on_open_file = [&](auto file_url) {
|
||||
auto& window = app->active_window();
|
||||
window.view().load(file_url);
|
||||
};
|
||||
|
||||
#if defined(AK_OS_MACOS)
|
||||
auto mach_port_server = make<Ladybird::MachPortServer>();
|
||||
set_mach_server_name(mach_port_server->server_port_name());
|
||||
mach_port_server->on_receive_child_mach_port = [&webview_app](auto pid, auto port) {
|
||||
webview_app.set_process_mach_port(pid, move(port));
|
||||
mach_port_server->on_receive_child_mach_port = [&app](auto pid, auto port) {
|
||||
app->set_process_mach_port(pid, move(port));
|
||||
};
|
||||
mach_port_server->on_receive_backing_stores = [](Ladybird::MachPortServer::BackingStoresMessage message) {
|
||||
if (auto view = WebView::WebContentClient::view_for_pid_and_page_id(message.pid, message.page_id); view.has_value())
|
||||
@ -156,40 +116,25 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||
copy_default_config_files(Ladybird::Settings::the()->directory());
|
||||
|
||||
RefPtr<WebView::Database> database;
|
||||
if (!disable_sql_database)
|
||||
if (app->chrome_options().disable_sql_database == WebView::DisableSQLDatabase::No)
|
||||
database = TRY(WebView::Database::create());
|
||||
|
||||
auto cookie_jar = database ? TRY(WebView::CookieJar::create(*database)) : WebView::CookieJar::create();
|
||||
|
||||
// FIXME: Create an abstraction to re-spawn the RequestServer and re-hook up its client hooks to each tab on crash
|
||||
if (!enable_qt_networking) {
|
||||
if (app->web_content_options().use_lagom_networking == WebView::UseLagomNetworking::Yes) {
|
||||
auto request_server_paths = TRY(get_paths_for_helper_process("RequestServer"sv));
|
||||
auto protocol_client = TRY(launch_request_server_process(request_server_paths, s_ladybird_resource_root, certificates));
|
||||
app.request_server_client = move(protocol_client);
|
||||
auto protocol_client = TRY(launch_request_server_process(request_server_paths, s_ladybird_resource_root));
|
||||
app->request_server_client = move(protocol_client);
|
||||
}
|
||||
|
||||
TRY(app.initialize_image_decoder());
|
||||
|
||||
StringBuilder command_line_builder;
|
||||
command_line_builder.join(' ', arguments.strings);
|
||||
Ladybird::WebContentOptions web_content_options {
|
||||
.command_line = MUST(command_line_builder.to_string()),
|
||||
.executable_path = MUST(String::from_byte_string(MUST(Core::System::current_executable_path()))),
|
||||
.config_path = Ladybird::Settings::the()->directory(),
|
||||
.enable_callgrind_profiling = enable_callgrind_profiling ? Ladybird::EnableCallgrindProfiling::Yes : Ladybird::EnableCallgrindProfiling::No,
|
||||
.use_lagom_networking = enable_qt_networking ? Ladybird::UseLagomNetworking::No : Ladybird::UseLagomNetworking::Yes,
|
||||
.wait_for_debugger = debug_web_content ? Ladybird::WaitForDebugger::Yes : Ladybird::WaitForDebugger::No,
|
||||
.log_all_js_exceptions = log_all_js_exceptions ? Ladybird::LogAllJSExceptions::Yes : Ladybird::LogAllJSExceptions::No,
|
||||
.enable_idl_tracing = enable_idl_tracing ? Ladybird::EnableIDLTracing::Yes : Ladybird::EnableIDLTracing::No,
|
||||
.enable_http_cache = enable_http_cache ? Ladybird::EnableHTTPCache::Yes : Ladybird::EnableHTTPCache::No,
|
||||
.expose_internals_object = expose_internals_object ? Ladybird::ExposeInternalsObject::Yes : Ladybird::ExposeInternalsObject::No,
|
||||
};
|
||||
TRY(app->initialize_image_decoder());
|
||||
|
||||
chrome_process.on_new_window = [&](auto const& urls) {
|
||||
app.new_window(sanitize_urls(urls), *cookie_jar, web_content_options, webdriver_content_ipc_path, allow_popups);
|
||||
app->new_window(urls, *cookie_jar);
|
||||
};
|
||||
|
||||
auto& window = app.new_window(sanitize_urls(raw_urls), *cookie_jar, web_content_options, webdriver_content_ipc_path, allow_popups);
|
||||
auto& window = app->new_window(app->chrome_options().urls, *cookie_jar);
|
||||
window.setWindowTitle("Ladybird");
|
||||
|
||||
if (Ladybird::Settings::the()->is_maximized()) {
|
||||
@ -203,5 +148,5 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||
|
||||
window.show();
|
||||
|
||||
return webview_app.exec();
|
||||
return app->execute();
|
||||
}
|
||||
|
@ -5,15 +5,17 @@
|
||||
*/
|
||||
|
||||
#include <AK/Debug.h>
|
||||
#include <LibCore/ArgsParser.h>
|
||||
#include <LibImageDecoderClient/Client.h>
|
||||
#include <LibWebView/Application.h>
|
||||
#include <LibWebView/URL.h>
|
||||
#include <LibWebView/WebContentClient.h>
|
||||
|
||||
namespace WebView {
|
||||
|
||||
Application* Application::s_the = nullptr;
|
||||
|
||||
Application::Application(int, char**)
|
||||
Application::Application()
|
||||
{
|
||||
VERIFY(!s_the);
|
||||
s_the = this;
|
||||
@ -28,7 +30,70 @@ Application::~Application()
|
||||
s_the = nullptr;
|
||||
}
|
||||
|
||||
int Application::exec()
|
||||
void Application::initialize(Main::Arguments const& arguments, URL::URL new_tab_page_url)
|
||||
{
|
||||
Vector<ByteString> raw_urls;
|
||||
Vector<ByteString> certificates;
|
||||
bool new_window = false;
|
||||
bool force_new_process = false;
|
||||
bool allow_popups = false;
|
||||
bool disable_sql_database = false;
|
||||
Optional<StringView> webdriver_content_ipc_path;
|
||||
bool enable_callgrind_profiling = false;
|
||||
bool debug_web_content = false;
|
||||
bool log_all_js_exceptions = false;
|
||||
bool enable_idl_tracing = false;
|
||||
bool enable_http_cache = false;
|
||||
bool expose_internals_object = false;
|
||||
|
||||
Core::ArgsParser args_parser;
|
||||
args_parser.set_general_help("The Ladybird web browser :^)");
|
||||
args_parser.add_positional_argument(raw_urls, "URLs to open", "url", Core::ArgsParser::Required::No);
|
||||
args_parser.add_option(certificates, "Path to a certificate file", "certificate", 'C', "certificate");
|
||||
args_parser.add_option(new_window, "Force opening in a new window", "new-window", 'n');
|
||||
args_parser.add_option(force_new_process, "Force creation of new browser/chrome process", "force-new-process");
|
||||
args_parser.add_option(allow_popups, "Disable popup blocking by default", "allow-popups");
|
||||
args_parser.add_option(disable_sql_database, "Disable SQL database", "disable-sql-database");
|
||||
args_parser.add_option(webdriver_content_ipc_path, "Path to WebDriver IPC for WebContent", "webdriver-content-path", 0, "path", Core::ArgsParser::OptionHideMode::CommandLineAndMarkdown);
|
||||
args_parser.add_option(enable_callgrind_profiling, "Enable Callgrind profiling", "enable-callgrind-profiling", 'P');
|
||||
args_parser.add_option(debug_web_content, "Wait for debugger to attach to WebContent", "debug-web-content");
|
||||
args_parser.add_option(log_all_js_exceptions, "Log all JavaScript exceptions", "log-all-js-exceptions");
|
||||
args_parser.add_option(enable_idl_tracing, "Enable IDL tracing", "enable-idl-tracing");
|
||||
args_parser.add_option(enable_http_cache, "Enable HTTP cache", "enable-http-cache");
|
||||
args_parser.add_option(expose_internals_object, "Expose internals object", "expose-internals-object");
|
||||
|
||||
create_platform_arguments(args_parser);
|
||||
args_parser.parse(arguments);
|
||||
|
||||
m_chrome_options = {
|
||||
.urls = sanitize_urls(raw_urls, new_tab_page_url),
|
||||
.raw_urls = move(raw_urls),
|
||||
.new_tab_page_url = move(new_tab_page_url),
|
||||
.certificates = move(certificates),
|
||||
.new_window = new_window ? NewWindow::Yes : NewWindow::No,
|
||||
.force_new_process = force_new_process ? ForceNewProcess::Yes : ForceNewProcess::No,
|
||||
.allow_popups = allow_popups ? AllowPopups::Yes : AllowPopups::No,
|
||||
.disable_sql_database = disable_sql_database ? DisableSQLDatabase::Yes : DisableSQLDatabase::No,
|
||||
};
|
||||
|
||||
if (webdriver_content_ipc_path.has_value())
|
||||
m_chrome_options.webdriver_content_ipc_path = *webdriver_content_ipc_path;
|
||||
|
||||
m_web_content_options = {
|
||||
.command_line = MUST(String::join(' ', arguments.strings)),
|
||||
.executable_path = MUST(String::from_byte_string(MUST(Core::System::current_executable_path()))),
|
||||
.enable_callgrind_profiling = enable_callgrind_profiling ? EnableCallgrindProfiling::Yes : EnableCallgrindProfiling::No,
|
||||
.wait_for_debugger = debug_web_content ? WaitForDebugger::Yes : WaitForDebugger::No,
|
||||
.log_all_js_exceptions = log_all_js_exceptions ? LogAllJSExceptions::Yes : LogAllJSExceptions::No,
|
||||
.enable_idl_tracing = enable_idl_tracing ? EnableIDLTracing::Yes : EnableIDLTracing::No,
|
||||
.enable_http_cache = enable_http_cache ? EnableHTTPCache::Yes : EnableHTTPCache::No,
|
||||
.expose_internals_object = expose_internals_object ? ExposeInternalsObject::Yes : ExposeInternalsObject::No,
|
||||
};
|
||||
|
||||
create_platform_options(m_chrome_options, m_web_content_options);
|
||||
}
|
||||
|
||||
int Application::execute()
|
||||
{
|
||||
int ret = m_event_loop.exec();
|
||||
m_in_shutdown = true;
|
||||
|
@ -6,7 +6,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Badge.h>
|
||||
#include <LibCore/EventLoop.h>
|
||||
#include <LibMain/Main.h>
|
||||
#include <LibURL/URL.h>
|
||||
#include <LibWebView/Options.h>
|
||||
#include <LibWebView/Process.h>
|
||||
#include <LibWebView/ProcessManager.h>
|
||||
|
||||
@ -22,13 +26,15 @@ class Application {
|
||||
AK_MAKE_NONCOPYABLE(Application);
|
||||
|
||||
public:
|
||||
Application(int argc, char** argv);
|
||||
virtual ~Application();
|
||||
|
||||
int exec();
|
||||
int execute();
|
||||
|
||||
static Application& the() { return *s_the; }
|
||||
|
||||
static ChromeOptions const& chrome_options() { return the().m_chrome_options; }
|
||||
static WebContentOptions const& web_content_options() { return the().m_web_content_options; }
|
||||
|
||||
Core::EventLoop& event_loop() { return m_event_loop; }
|
||||
|
||||
void add_child_process(Process&&);
|
||||
@ -44,14 +50,42 @@ public:
|
||||
String generate_process_statistics_html();
|
||||
|
||||
protected:
|
||||
template<DerivedFrom<Application> ApplicationType>
|
||||
static NonnullOwnPtr<ApplicationType> create(Main::Arguments& arguments, URL::URL new_tab_page_url)
|
||||
{
|
||||
auto app = adopt_own(*new ApplicationType { {}, arguments });
|
||||
app->initialize(arguments, move(new_tab_page_url));
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
Application();
|
||||
|
||||
virtual void process_did_exit(Process&&);
|
||||
|
||||
virtual void create_platform_arguments(Core::ArgsParser&) { }
|
||||
virtual void create_platform_options(ChromeOptions&, WebContentOptions&) { }
|
||||
|
||||
private:
|
||||
void initialize(Main::Arguments const& arguments, URL::URL new_tab_page_url);
|
||||
|
||||
static Application* s_the;
|
||||
|
||||
ChromeOptions m_chrome_options;
|
||||
WebContentOptions m_web_content_options;
|
||||
|
||||
Core::EventLoop m_event_loop;
|
||||
ProcessManager m_process_manager;
|
||||
bool m_in_shutdown { false };
|
||||
} SWIFT_IMMORTAL_REFERENCE;
|
||||
|
||||
}
|
||||
|
||||
#define WEB_VIEW_APPLICATION(ApplicationType) \
|
||||
public: \
|
||||
static NonnullOwnPtr<ApplicationType> create(Main::Arguments& arguments, URL::URL new_tab_page_url) \
|
||||
{ \
|
||||
return WebView::Application::create<ApplicationType>(arguments, move(new_tab_page_url)); \
|
||||
} \
|
||||
\
|
||||
ApplicationType(Badge<WebView::Application>, Main::Arguments&);
|
||||
|
@ -9,7 +9,9 @@
|
||||
#include <LibCore/StandardPaths.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <LibIPC/ConnectionToServer.h>
|
||||
#include <LibWebView/Application.h>
|
||||
#include <LibWebView/ChromeProcess.h>
|
||||
#include <LibWebView/URL.h>
|
||||
|
||||
namespace WebView {
|
||||
|
||||
@ -32,7 +34,7 @@ ErrorOr<ChromeProcess> ChromeProcess::create()
|
||||
return ChromeProcess {};
|
||||
}
|
||||
|
||||
ErrorOr<ChromeProcess::ProcessDisposition> ChromeProcess::connect(Vector<ByteString> const& raw_urls, bool new_window)
|
||||
ErrorOr<ChromeProcess::ProcessDisposition> ChromeProcess::connect(Vector<ByteString> const& raw_urls, NewWindow new_window)
|
||||
{
|
||||
static constexpr auto process_name = "Ladybird"sv;
|
||||
|
||||
@ -52,12 +54,12 @@ ErrorOr<ChromeProcess::ProcessDisposition> ChromeProcess::connect(Vector<ByteStr
|
||||
return ProcessDisposition::ContinueMainProcess;
|
||||
}
|
||||
|
||||
ErrorOr<void> ChromeProcess::connect_as_client(ByteString const& socket_path, Vector<ByteString> const& raw_urls, bool new_window)
|
||||
ErrorOr<void> ChromeProcess::connect_as_client(ByteString const& socket_path, Vector<ByteString> const& raw_urls, NewWindow new_window)
|
||||
{
|
||||
auto socket = TRY(Core::LocalSocket::connect(socket_path));
|
||||
auto client = UIProcessClient::construct(move(socket));
|
||||
|
||||
if (new_window) {
|
||||
if (new_window == NewWindow::Yes) {
|
||||
if (!client->send_sync_but_allow_failure<Messages::UIProcessServer::CreateNewWindow>(raw_urls))
|
||||
dbgln("Failed to send CreateNewWindow message to UIProcess");
|
||||
} else {
|
||||
@ -121,13 +123,13 @@ void UIProcessConnectionFromClient::die()
|
||||
void UIProcessConnectionFromClient::create_new_tab(Vector<ByteString> const& urls)
|
||||
{
|
||||
if (on_new_tab)
|
||||
on_new_tab(urls);
|
||||
on_new_tab(sanitize_urls(urls, Application::chrome_options().new_tab_page_url));
|
||||
}
|
||||
|
||||
void UIProcessConnectionFromClient::create_new_window(Vector<ByteString> const& urls)
|
||||
{
|
||||
if (on_new_window)
|
||||
on_new_window(urls);
|
||||
on_new_window(sanitize_urls(urls, Application::chrome_options().new_tab_page_url));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <LibIPC/ConnectionFromClient.h>
|
||||
#include <LibIPC/Forward.h>
|
||||
#include <LibIPC/MultiServer.h>
|
||||
#include <LibWebView/Options.h>
|
||||
#include <LibWebView/UIProcessClientEndpoint.h>
|
||||
#include <LibWebView/UIProcessServerEndpoint.h>
|
||||
|
||||
@ -28,8 +29,8 @@ public:
|
||||
|
||||
virtual void die() override;
|
||||
|
||||
Function<void(Vector<ByteString> const& urls)> on_new_tab;
|
||||
Function<void(Vector<ByteString> const& urls)> on_new_window;
|
||||
Function<void(Vector<URL::URL> const&)> on_new_tab;
|
||||
Function<void(Vector<URL::URL> const&)> on_new_window;
|
||||
|
||||
private:
|
||||
UIProcessConnectionFromClient(NonnullOwnPtr<Core::LocalSocket>, int client_id);
|
||||
@ -51,15 +52,15 @@ public:
|
||||
static ErrorOr<ChromeProcess> create();
|
||||
~ChromeProcess();
|
||||
|
||||
ErrorOr<ProcessDisposition> connect(Vector<ByteString> const& raw_urls, bool new_window);
|
||||
ErrorOr<ProcessDisposition> connect(Vector<ByteString> const& raw_urls, NewWindow new_window);
|
||||
|
||||
Function<void(Vector<ByteString> const& raw_urls)> on_new_tab;
|
||||
Function<void(Vector<ByteString> const& raw_urls)> on_new_window;
|
||||
Function<void(Vector<URL::URL> const&)> on_new_tab;
|
||||
Function<void(Vector<URL::URL> const&)> on_new_window;
|
||||
|
||||
private:
|
||||
ChromeProcess() = default;
|
||||
|
||||
ErrorOr<void> connect_as_client(ByteString const& socket_path, Vector<ByteString> const& raw_urls, bool new_window);
|
||||
ErrorOr<void> connect_as_client(ByteString const& socket_path, Vector<ByteString> const& raw_urls, NewWindow new_window);
|
||||
ErrorOr<void> connect_as_server(ByteString const& socket_path);
|
||||
|
||||
OwnPtr<IPC::MultiServer<UIProcessConnectionFromClient>> m_server_connection;
|
||||
|
@ -6,48 +6,84 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibURL/URL.h>
|
||||
|
||||
namespace Ladybird {
|
||||
namespace WebView {
|
||||
|
||||
enum class NewWindow {
|
||||
No,
|
||||
Yes,
|
||||
};
|
||||
|
||||
enum class ForceNewProcess {
|
||||
No,
|
||||
Yes,
|
||||
};
|
||||
|
||||
enum class AllowPopups {
|
||||
No,
|
||||
Yes,
|
||||
};
|
||||
|
||||
enum class DisableSQLDatabase {
|
||||
No,
|
||||
Yes,
|
||||
};
|
||||
|
||||
struct ChromeOptions {
|
||||
Vector<URL::URL> urls;
|
||||
Vector<ByteString> raw_urls;
|
||||
URL::URL new_tab_page_url;
|
||||
Vector<ByteString> certificates {};
|
||||
NewWindow new_window { NewWindow::No };
|
||||
ForceNewProcess force_new_process { ForceNewProcess::No };
|
||||
AllowPopups allow_popups { AllowPopups::No };
|
||||
DisableSQLDatabase disable_sql_database { DisableSQLDatabase::No };
|
||||
Optional<ByteString> webdriver_content_ipc_path {};
|
||||
};
|
||||
|
||||
enum class EnableCallgrindProfiling {
|
||||
No,
|
||||
Yes
|
||||
Yes,
|
||||
};
|
||||
|
||||
enum class IsLayoutTestMode {
|
||||
No,
|
||||
Yes
|
||||
Yes,
|
||||
};
|
||||
|
||||
enum class UseLagomNetworking {
|
||||
No,
|
||||
Yes
|
||||
Yes,
|
||||
};
|
||||
|
||||
enum class WaitForDebugger {
|
||||
No,
|
||||
Yes
|
||||
Yes,
|
||||
};
|
||||
|
||||
enum class LogAllJSExceptions {
|
||||
No,
|
||||
Yes
|
||||
Yes,
|
||||
};
|
||||
|
||||
enum class EnableIDLTracing {
|
||||
No,
|
||||
Yes
|
||||
Yes,
|
||||
};
|
||||
|
||||
enum class EnableHTTPCache {
|
||||
No,
|
||||
Yes
|
||||
Yes,
|
||||
};
|
||||
|
||||
enum class ExposeInternalsObject {
|
||||
No,
|
||||
Yes
|
||||
Yes,
|
||||
};
|
||||
|
||||
struct WebContentOptions {
|
@ -72,6 +72,22 @@ Optional<URL::URL> sanitize_url(StringView url, Optional<StringView> search_engi
|
||||
return result;
|
||||
}
|
||||
|
||||
Vector<URL::URL> sanitize_urls(ReadonlySpan<ByteString> raw_urls, URL::URL const& new_tab_page_url)
|
||||
{
|
||||
Vector<URL::URL> sanitized_urls;
|
||||
sanitized_urls.ensure_capacity(raw_urls.size());
|
||||
|
||||
for (auto const& raw_url : raw_urls) {
|
||||
if (auto url = sanitize_url(raw_url); url.has_value())
|
||||
sanitized_urls.unchecked_append(url.release_value());
|
||||
}
|
||||
|
||||
if (sanitized_urls.is_empty())
|
||||
sanitized_urls.append(new_tab_page_url);
|
||||
|
||||
return sanitized_urls;
|
||||
}
|
||||
|
||||
static URLParts break_file_url_into_parts(URL::URL const& url, StringView url_string)
|
||||
{
|
||||
auto scheme = url_string.substring_view(0, url.scheme().bytes_as_string_view().length() + "://"sv.length());
|
||||
|
@ -20,6 +20,7 @@ enum class AppendTLD {
|
||||
Yes,
|
||||
};
|
||||
Optional<URL::URL> sanitize_url(StringView, Optional<StringView> search_engine = {}, AppendTLD = AppendTLD::No);
|
||||
Vector<URL::URL> sanitize_urls(ReadonlySpan<ByteString> raw_urls, URL::URL const& new_tab_page_url);
|
||||
|
||||
struct URLParts {
|
||||
StringView scheme_and_subdomain;
|
||||
|
@ -19,7 +19,6 @@
|
||||
#include <AK/String.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <Ladybird/HelperProcess.h>
|
||||
#include <Ladybird/Types.h>
|
||||
#include <Ladybird/Utilities.h>
|
||||
#include <LibCore/ArgsParser.h>
|
||||
#include <LibCore/ConfigFile.h>
|
||||
@ -64,13 +63,13 @@ static StringView s_current_test_path;
|
||||
|
||||
class HeadlessWebContentView final : public WebView::ViewImplementation {
|
||||
public:
|
||||
static ErrorOr<NonnullOwnPtr<HeadlessWebContentView>> create(Core::AnonymousBuffer theme, Gfx::IntSize const& window_size, String const& command_line, StringView web_driver_ipc_path, Ladybird::IsLayoutTestMode is_layout_test_mode = Ladybird::IsLayoutTestMode::No, Vector<ByteString> const& certificates = {}, StringView resources_folder = {})
|
||||
static ErrorOr<NonnullOwnPtr<HeadlessWebContentView>> create(Core::AnonymousBuffer theme, Gfx::IntSize const& window_size, StringView resources_folder)
|
||||
{
|
||||
RefPtr<Protocol::RequestClient> request_client;
|
||||
RefPtr<ImageDecoderClient::Client> image_decoder_client;
|
||||
|
||||
auto request_server_paths = TRY(get_paths_for_helper_process("RequestServer"sv));
|
||||
request_client = TRY(launch_request_server_process(request_server_paths, resources_folder, certificates));
|
||||
request_client = TRY(launch_request_server_process(request_server_paths, resources_folder));
|
||||
|
||||
auto image_decoder_paths = TRY(get_paths_for_helper_process("ImageDecoder"sv));
|
||||
image_decoder_client = TRY(launch_image_decoder_process(image_decoder_paths));
|
||||
@ -80,17 +79,11 @@ public:
|
||||
|
||||
auto view = TRY(adopt_nonnull_own_or_enomem(new (nothrow) HeadlessWebContentView(move(database), move(cookie_jar), image_decoder_client, request_client)));
|
||||
|
||||
Ladybird::WebContentOptions web_content_options {
|
||||
.command_line = command_line,
|
||||
.executable_path = MUST(String::from_byte_string(MUST(Core::System::current_executable_path()))),
|
||||
.is_layout_test_mode = is_layout_test_mode,
|
||||
};
|
||||
|
||||
auto request_server_socket = TRY(connect_new_request_server_client(*request_client));
|
||||
auto image_decoder_socket = TRY(connect_new_image_decoder_client(*image_decoder_client));
|
||||
|
||||
auto candidate_web_content_paths = TRY(get_paths_for_helper_process("WebContent"sv));
|
||||
view->m_client_state.client = TRY(launch_web_content_process(*view, candidate_web_content_paths, web_content_options, move(image_decoder_socket), move(request_server_socket)));
|
||||
view->m_client_state.client = TRY(launch_web_content_process(*view, candidate_web_content_paths, move(image_decoder_socket), move(request_server_socket)));
|
||||
|
||||
view->client().async_update_system_theme(0, move(theme));
|
||||
|
||||
@ -98,8 +91,8 @@ public:
|
||||
view->client().async_set_viewport_size(0, view->m_viewport_size.to_type<Web::DevicePixels>());
|
||||
view->client().async_set_window_size(0, window_size.to_type<Web::DevicePixels>());
|
||||
|
||||
if (!web_driver_ipc_path.is_empty())
|
||||
view->client().async_connect_to_webdriver(0, web_driver_ipc_path);
|
||||
if (auto web_driver_ipc_path = WebView::Application::chrome_options().webdriver_content_ipc_path; web_driver_ipc_path.has_value())
|
||||
view->client().async_connect_to_webdriver(0, *web_driver_ipc_path);
|
||||
|
||||
view->m_client_state.client->on_web_content_process_crash = [] {
|
||||
warnln("\033[31;1mWebContent Crashed!!\033[0m");
|
||||
@ -189,7 +182,7 @@ private:
|
||||
RefPtr<ImageDecoderClient::Client> m_image_decoder_client;
|
||||
};
|
||||
|
||||
static ErrorOr<NonnullRefPtr<Core::Timer>> load_page_for_screenshot_and_exit(Core::EventLoop& event_loop, HeadlessWebContentView& view, URL::URL url, int screenshot_timeout)
|
||||
static ErrorOr<NonnullRefPtr<Core::Timer>> load_page_for_screenshot_and_exit(Core::EventLoop& event_loop, HeadlessWebContentView& view, URL::URL const& url, int screenshot_timeout)
|
||||
{
|
||||
// FIXME: Allow passing the output path as an argument.
|
||||
static constexpr auto output_file_path = "output.png"sv;
|
||||
@ -248,7 +241,7 @@ static StringView test_result_to_string(TestResult result)
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
static ErrorOr<TestResult> run_dump_test(HeadlessWebContentView& view, StringView input_path, StringView expectation_path, TestMode mode, int timeout_in_milliseconds = DEFAULT_TIMEOUT_MS)
|
||||
static ErrorOr<TestResult> run_dump_test(HeadlessWebContentView& view, URL::URL const& url, StringView expectation_path, TestMode mode, int timeout_in_milliseconds = DEFAULT_TIMEOUT_MS)
|
||||
{
|
||||
Core::EventLoop loop;
|
||||
bool did_timeout = false;
|
||||
@ -258,8 +251,6 @@ static ErrorOr<TestResult> run_dump_test(HeadlessWebContentView& view, StringVie
|
||||
loop.quit(0);
|
||||
});
|
||||
|
||||
auto url = URL::create_with_file_scheme(TRY(FileSystem::real_path(input_path)));
|
||||
|
||||
String result;
|
||||
auto did_finish_test = false;
|
||||
auto did_finish_loading = false;
|
||||
@ -335,9 +326,9 @@ static ErrorOr<TestResult> run_dump_test(HeadlessWebContentView& view, StringVie
|
||||
auto const color_output = isatty(STDOUT_FILENO) ? Diff::ColorOutput::Yes : Diff::ColorOutput::No;
|
||||
|
||||
if (color_output == Diff::ColorOutput::Yes)
|
||||
outln("\n\033[33;1mTest failed\033[0m: {}", input_path);
|
||||
outln("\n\033[33;1mTest failed\033[0m: {}", url);
|
||||
else
|
||||
outln("\nTest failed: {}", input_path);
|
||||
outln("\nTest failed: {}", url);
|
||||
|
||||
auto hunks = TRY(Diff::from_text(expectation, actual, 3));
|
||||
auto out = TRY(Core::File::standard_output());
|
||||
@ -349,7 +340,7 @@ static ErrorOr<TestResult> run_dump_test(HeadlessWebContentView& view, StringVie
|
||||
return TestResult::Fail;
|
||||
}
|
||||
|
||||
static ErrorOr<TestResult> run_ref_test(HeadlessWebContentView& view, StringView input_path, bool dump_failed_ref_tests, int timeout_in_milliseconds = DEFAULT_TIMEOUT_MS)
|
||||
static ErrorOr<TestResult> run_ref_test(HeadlessWebContentView& view, URL::URL const& url, bool dump_failed_ref_tests, int timeout_in_milliseconds = DEFAULT_TIMEOUT_MS)
|
||||
{
|
||||
Core::EventLoop loop;
|
||||
bool did_timeout = false;
|
||||
@ -370,10 +361,10 @@ static ErrorOr<TestResult> run_ref_test(HeadlessWebContentView& view, StringView
|
||||
}
|
||||
};
|
||||
view.on_text_test_finish = [&] {
|
||||
dbgln("Unexpected text test finished during ref test for {}", input_path);
|
||||
dbgln("Unexpected text test finished during ref test for {}", url);
|
||||
};
|
||||
|
||||
view.load(URL::create_with_file_scheme(TRY(FileSystem::real_path(input_path))));
|
||||
view.load(url);
|
||||
|
||||
timeout_timer->start();
|
||||
loop.exec();
|
||||
@ -388,8 +379,8 @@ static ErrorOr<TestResult> run_ref_test(HeadlessWebContentView& view, StringView
|
||||
return TestResult::Pass;
|
||||
|
||||
if (dump_failed_ref_tests) {
|
||||
warnln("\033[33;1mRef test {} failed; dumping screenshots\033[0m", input_path);
|
||||
auto title = LexicalPath::title(input_path);
|
||||
warnln("\033[33;1mRef test {} failed; dumping screenshots\033[0m", url);
|
||||
auto title = LexicalPath::title(url.serialize_path());
|
||||
auto dump_screenshot = [&](Gfx::Bitmap& bitmap, StringView path) -> ErrorOr<void> {
|
||||
auto screenshot_file = TRY(Core::File::open(path, Core::File::OpenMode::Write));
|
||||
auto encoded_data = TRY(Gfx::PNGWriter::encode(bitmap));
|
||||
@ -462,13 +453,15 @@ static ErrorOr<TestResult> run_test(HeadlessWebContentView& view, StringView inp
|
||||
view.load(URL::URL("about:blank"sv));
|
||||
MUST(promise->await());
|
||||
|
||||
auto url = URL::create_with_file_scheme(TRY(FileSystem::real_path(input_path)));
|
||||
s_current_test_path = input_path;
|
||||
|
||||
switch (mode) {
|
||||
case TestMode::Text:
|
||||
case TestMode::Layout:
|
||||
return run_dump_test(view, input_path, expectation_path, mode);
|
||||
return run_dump_test(view, url, expectation_path, mode);
|
||||
case TestMode::Ref:
|
||||
return run_ref_test(view, input_path, dump_failed_ref_tests);
|
||||
return run_ref_test(view, url, dump_failed_ref_tests);
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
@ -633,83 +626,88 @@ static ErrorOr<int> run_tests(HeadlessWebContentView& view, StringView test_root
|
||||
return 1;
|
||||
}
|
||||
|
||||
ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||
{
|
||||
WebView::Application app(arguments.argc, arguments.argv);
|
||||
struct Application : public WebView::Application {
|
||||
WEB_VIEW_APPLICATION(Application)
|
||||
|
||||
int screenshot_timeout = 1;
|
||||
StringView raw_url;
|
||||
auto resources_folder = "/res"sv;
|
||||
StringView web_driver_ipc_path;
|
||||
bool dump_failed_ref_tests = false;
|
||||
bool dump_layout_tree = false;
|
||||
bool dump_text = false;
|
||||
bool dump_gc_graph = false;
|
||||
bool is_layout_test_mode = false;
|
||||
virtual void create_platform_arguments(Core::ArgsParser& args_parser) override
|
||||
{
|
||||
args_parser.add_option(screenshot_timeout, "Take a screenshot after [n] seconds (default: 1)", "screenshot", 's', "n");
|
||||
args_parser.add_option(dump_layout_tree, "Dump layout tree and exit", "dump-layout-tree", 'd');
|
||||
args_parser.add_option(dump_text, "Dump text and exit", "dump-text", 'T');
|
||||
args_parser.add_option(test_root_path, "Run tests in path", "run-tests", 'R', "test-root-path");
|
||||
args_parser.add_option(test_glob, "Only run tests matching the given glob", "filter", 'f', "glob");
|
||||
args_parser.add_option(dump_failed_ref_tests, "Dump screenshots of failing ref tests", "dump-failed-ref-tests", 'D');
|
||||
args_parser.add_option(dump_gc_graph, "Dump GC graph", "dump-gc-graph", 'G');
|
||||
args_parser.add_option(resources_folder, "Path of the base resources folder (defaults to /res)", "resources", 'r', "resources-root-path");
|
||||
args_parser.add_option(is_layout_test_mode, "Enable layout test mode", "layout-test-mode");
|
||||
}
|
||||
|
||||
virtual void create_platform_options(WebView::ChromeOptions&, WebView::WebContentOptions& web_content_options) override
|
||||
{
|
||||
if (!test_root_path.is_empty()) {
|
||||
// --run-tests implies --layout-test-mode.
|
||||
is_layout_test_mode = true;
|
||||
}
|
||||
|
||||
web_content_options.is_layout_test_mode = is_layout_test_mode ? WebView::IsLayoutTestMode::Yes : WebView::IsLayoutTestMode::No;
|
||||
}
|
||||
|
||||
int screenshot_timeout { 1 };
|
||||
ByteString resources_folder { s_ladybird_resource_root };
|
||||
bool dump_failed_ref_tests { false };
|
||||
bool dump_layout_tree { false };
|
||||
bool dump_text { false };
|
||||
bool dump_gc_graph { false };
|
||||
bool is_layout_test_mode { false };
|
||||
StringView test_root_path;
|
||||
ByteString test_glob;
|
||||
Vector<ByteString> certificates;
|
||||
};
|
||||
|
||||
Application::Application(Badge<WebView::Application>, Main::Arguments&)
|
||||
{
|
||||
}
|
||||
|
||||
ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||
{
|
||||
platform_init();
|
||||
resources_folder = s_ladybird_resource_root;
|
||||
|
||||
Core::ArgsParser args_parser;
|
||||
args_parser.set_general_help("This utility runs the Browser in headless mode.");
|
||||
args_parser.add_option(screenshot_timeout, "Take a screenshot after [n] seconds (default: 1)", "screenshot", 's', "n");
|
||||
args_parser.add_option(dump_layout_tree, "Dump layout tree and exit", "dump-layout-tree", 'd');
|
||||
args_parser.add_option(dump_text, "Dump text and exit", "dump-text", 'T');
|
||||
args_parser.add_option(test_root_path, "Run tests in path", "run-tests", 'R', "test-root-path");
|
||||
args_parser.add_option(test_glob, "Only run tests matching the given glob", "filter", 'f', "glob");
|
||||
args_parser.add_option(dump_failed_ref_tests, "Dump screenshots of failing ref tests", "dump-failed-ref-tests", 'D');
|
||||
args_parser.add_option(dump_gc_graph, "Dump GC graph", "dump-gc-graph", 'G');
|
||||
args_parser.add_option(resources_folder, "Path of the base resources folder (defaults to /res)", "resources", 'r', "resources-root-path");
|
||||
args_parser.add_option(web_driver_ipc_path, "Path to the WebDriver IPC socket", "webdriver-ipc-path", 0, "path");
|
||||
args_parser.add_option(is_layout_test_mode, "Enable layout test mode", "layout-test-mode");
|
||||
args_parser.add_option(certificates, "Path to a certificate file", "certificate", 'C', "certificate");
|
||||
args_parser.add_positional_argument(raw_url, "URL to open", "url", Core::ArgsParser::Required::No);
|
||||
args_parser.parse(arguments);
|
||||
auto app = Application::create(arguments, "about:newtab"sv);
|
||||
|
||||
Core::ResourceImplementation::install(make<Core::ResourceImplementationFile>(MUST(String::from_utf8(resources_folder))));
|
||||
Core::ResourceImplementation::install(make<Core::ResourceImplementationFile>(MUST(String::from_byte_string(app->resources_folder))));
|
||||
|
||||
auto theme_path = LexicalPath::join(resources_folder, "themes"sv, "Default.ini"sv);
|
||||
auto theme_path = LexicalPath::join(app->resources_folder, "themes"sv, "Default.ini"sv);
|
||||
auto theme = TRY(Gfx::load_system_theme(theme_path.string()));
|
||||
|
||||
// FIXME: Allow passing the window size as an argument.
|
||||
static constexpr Gfx::IntSize window_size { 800, 600 };
|
||||
|
||||
if (!test_root_path.is_empty()) {
|
||||
// --run-tests implies --layout-test-mode.
|
||||
is_layout_test_mode = true;
|
||||
auto view = TRY(HeadlessWebContentView::create(move(theme), window_size, app->resources_folder));
|
||||
|
||||
if (!app->test_root_path.is_empty()) {
|
||||
auto test_glob = ByteString::formatted("*{}*", app->test_glob);
|
||||
return run_tests(*view, app->test_root_path, test_glob, app->dump_failed_ref_tests, app->dump_gc_graph);
|
||||
}
|
||||
|
||||
StringBuilder command_line_builder;
|
||||
command_line_builder.join(' ', arguments.strings);
|
||||
auto view = TRY(HeadlessWebContentView::create(move(theme), window_size, MUST(command_line_builder.to_string()), web_driver_ipc_path, is_layout_test_mode ? Ladybird::IsLayoutTestMode::Yes : Ladybird::IsLayoutTestMode::No, certificates, resources_folder));
|
||||
|
||||
if (!test_root_path.is_empty()) {
|
||||
test_glob = ByteString::formatted("*{}*", test_glob);
|
||||
return run_tests(*view, test_root_path, test_glob, dump_failed_ref_tests, dump_gc_graph);
|
||||
}
|
||||
|
||||
auto url = WebView::sanitize_url(raw_url);
|
||||
if (!url.has_value()) {
|
||||
warnln("Invalid URL: \"{}\"", raw_url);
|
||||
VERIFY(!WebView::Application::chrome_options().urls.is_empty());
|
||||
auto const& url = WebView::Application::chrome_options().urls.first();
|
||||
if (!url.is_valid()) {
|
||||
warnln("Invalid URL: \"{}\"", url);
|
||||
return Error::from_string_literal("Invalid URL");
|
||||
}
|
||||
|
||||
if (dump_layout_tree) {
|
||||
TRY(run_dump_test(*view, raw_url, ""sv, TestMode::Layout));
|
||||
if (app->dump_layout_tree) {
|
||||
TRY(run_dump_test(*view, url, ""sv, TestMode::Layout));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dump_text) {
|
||||
TRY(run_dump_test(*view, raw_url, ""sv, TestMode::Text));
|
||||
if (app->dump_text) {
|
||||
TRY(run_dump_test(*view, url, ""sv, TestMode::Text));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (web_driver_ipc_path.is_empty()) {
|
||||
auto timer = TRY(load_page_for_screenshot_and_exit(Core::EventLoop::current(), *view, url.value(), screenshot_timeout));
|
||||
return app.exec();
|
||||
if (!WebView::Application::chrome_options().webdriver_content_ipc_path.has_value()) {
|
||||
auto timer = TRY(load_page_for_screenshot_and_exit(Core::EventLoop::current(), *view, url, app->screenshot_timeout));
|
||||
return app->execute();
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
Loading…
Reference in New Issue
Block a user