فهرست منبع

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.
Timothy Flynn 11 ماه پیش
والد
کامیت
5f8d852dae
35فایلهای تغییر یافته به همراه429 افزوده شده و 442 حذف شده
  1. 6 4
      Ladybird/AppKit/Application/Application.h
  2. 8 12
      Ladybird/AppKit/Application/Application.mm
  3. 4 5
      Ladybird/AppKit/Application/ApplicationBridge.cpp
  4. 5 4
      Ladybird/AppKit/Application/ApplicationBridge.h
  5. 2 11
      Ladybird/AppKit/Application/ApplicationDelegate.h
  6. 5 40
      Ladybird/AppKit/Application/ApplicationDelegate.mm
  7. 1 1
      Ladybird/AppKit/UI/LadybirdWebView.mm
  8. 7 9
      Ladybird/AppKit/UI/LadybirdWebViewBridge.cpp
  9. 3 9
      Ladybird/AppKit/UI/LadybirdWebViewBridge.h
  10. 2 2
      Ladybird/AppKit/UI/TabController.h
  11. 4 3
      Ladybird/AppKit/UI/TabController.mm
  12. 19 78
      Ladybird/AppKit/main.mm
  13. 0 1
      Ladybird/CMakeLists.txt
  14. 19 18
      Ladybird/HelperProcess.cpp
  15. 1 3
      Ladybird/HelperProcess.h
  16. 22 9
      Ladybird/Qt/Application.cpp
  17. 12 4
      Ladybird/Qt/Application.h
  18. 10 16
      Ladybird/Qt/BrowserWindow.cpp
  19. 1 6
      Ladybird/Qt/BrowserWindow.h
  20. 1 1
      Ladybird/Qt/InspectorWidget.cpp
  21. 2 2
      Ladybird/Qt/Tab.cpp
  22. 1 1
      Ladybird/Qt/Tab.h
  23. 2 2
      Ladybird/Qt/TaskManagerWindow.cpp
  24. 1 1
      Ladybird/Qt/TaskManagerWindow.h
  25. 7 8
      Ladybird/Qt/WebContentView.cpp
  26. 1 7
      Ladybird/Qt/WebContentView.h
  27. 25 80
      Ladybird/Qt/main.cpp
  28. 67 2
      Userland/Libraries/LibWebView/Application.cpp
  29. 36 2
      Userland/Libraries/LibWebView/Application.h
  30. 7 5
      Userland/Libraries/LibWebView/ChromeProcess.cpp
  31. 7 6
      Userland/Libraries/LibWebView/ChromeProcess.h
  32. 45 9
      Userland/Libraries/LibWebView/Options.h
  33. 16 0
      Userland/Libraries/LibWebView/URL.cpp
  34. 1 0
      Userland/Libraries/LibWebView/URL.h
  35. 79 81
      Userland/Utilities/headless-browser.cpp

+ 6 - 4
Ladybird/AppKit/Application/Application.h

@@ -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
  * SPDX-License-Identifier: BSD-2-Clause
  */
  */
@@ -7,8 +7,9 @@
 #pragma once
 #pragma once
 
 
 #include <AK/Error.h>
 #include <AK/Error.h>
-#include <AK/Vector.h>
 #include <LibIPC/Forward.h>
 #include <LibIPC/Forward.h>
+#include <LibMain/Main.h>
+#include <LibURL/URL.h>
 #include <LibWebView/Forward.h>
 #include <LibWebView/Forward.h>
 
 
 #import <Cocoa/Cocoa.h>
 #import <Cocoa/Cocoa.h>
@@ -19,9 +20,10 @@ class WebViewBridge;
 
 
 @interface Application : NSApplication
 @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<void>)launchImageDecoder;
 - (ErrorOr<NonnullRefPtr<WebView::WebContentClient>>)launchWebContent:(Ladybird::WebViewBridge&)web_view_bridge;
 - (ErrorOr<NonnullRefPtr<WebView::WebContentClient>>)launchWebContent:(Ladybird::WebViewBridge&)web_view_bridge;
 - (ErrorOr<IPC::File>)launchWebWorker;
 - (ErrorOr<IPC::File>)launchWebWorker;

+ 8 - 12
Ladybird/AppKit/Application/Application.mm

@@ -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
  * SPDX-License-Identifier: BSD-2-Clause
  */
  */
 
 
-#include <AK/ByteString.h>
 #include <Application/ApplicationBridge.h>
 #include <Application/ApplicationBridge.h>
 #include <LibCore/EventLoop.h>
 #include <LibCore/EventLoop.h>
 #include <LibCore/ThreadEventQueue.h>
 #include <LibCore/ThreadEventQueue.h>
@@ -25,20 +24,17 @@
 
 
 @implementation Application
 @implementation Application
 
 
-- (instancetype)init
-{
-    if (self = [super init]) {
-        m_application_bridge = make<Ladybird::ApplicationBridge>();
-    }
+#pragma mark - Public methods
 
 
-    return self;
+- (void)setupWebViewApplication:(Main::Arguments&)arguments
+                  newTabPageURL:(URL::URL)new_tab_page_url
+{
+    m_application_bridge = Ladybird::ApplicationBridge::create(arguments, move(new_tab_page_url));
 }
 }
 
 
-#pragma mark - Public methods
-
-- (ErrorOr<void>)launchRequestServer:(Vector<ByteString> const&)certificates
+- (ErrorOr<void>)launchRequestServer
 {
 {
-    return m_application_bridge->launch_request_server(certificates);
+    return m_application_bridge->launch_request_server();
 }
 }
 
 
 - (ErrorOr<void>)launchImageDecoder
 - (ErrorOr<void>)launchImageDecoder

+ 4 - 5
Ladybird/AppKit/Application/ApplicationBridge.cpp

@@ -4,7 +4,6 @@
  * SPDX-License-Identifier: BSD-2-Clause
  * SPDX-License-Identifier: BSD-2-Clause
  */
  */
 
 
-#include <AK/ByteString.h>
 #include <Application/ApplicationBridge.h>
 #include <Application/ApplicationBridge.h>
 #include <Ladybird/AppKit/UI/LadybirdWebViewBridge.h>
 #include <Ladybird/AppKit/UI/LadybirdWebViewBridge.h>
 #include <Ladybird/HelperProcess.h>
 #include <Ladybird/HelperProcess.h>
@@ -23,17 +22,17 @@ struct ApplicationBridgeImpl {
     RefPtr<ImageDecoderClient::Client> image_decoder_client;
     RefPtr<ImageDecoderClient::Client> image_decoder_client;
 };
 };
 
 
-ApplicationBridge::ApplicationBridge()
+ApplicationBridge::ApplicationBridge(Badge<WebView::Application>, Main::Arguments&)
     : m_impl(make<ApplicationBridgeImpl>())
     : m_impl(make<ApplicationBridgeImpl>())
 {
 {
 }
 }
 
 
 ApplicationBridge::~ApplicationBridge() = default;
 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 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);
     m_impl->request_server_client = move(protocol_client);
     return {};
     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 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_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;
     return web_content;
 }
 }

+ 5 - 4
Ladybird/AppKit/Application/ApplicationBridge.h

@@ -7,8 +7,8 @@
 #pragma once
 #pragma once
 
 
 #include <AK/NonnullOwnPtr.h>
 #include <AK/NonnullOwnPtr.h>
-#include <AK/Vector.h>
 #include <LibIPC/Forward.h>
 #include <LibIPC/Forward.h>
+#include <LibWebView/Application.h>
 #include <LibWebView/Forward.h>
 #include <LibWebView/Forward.h>
 
 
 namespace Ladybird {
 namespace Ladybird {
@@ -16,12 +16,13 @@ namespace Ladybird {
 struct ApplicationBridgeImpl;
 struct ApplicationBridgeImpl;
 class WebViewBridge;
 class WebViewBridge;
 
 
-class ApplicationBridge {
+class ApplicationBridge : public WebView::Application {
+    WEB_VIEW_APPLICATION(ApplicationBridge)
+
 public:
 public:
-    ApplicationBridge();
     ~ApplicationBridge();
     ~ApplicationBridge();
 
 
-    ErrorOr<void> launch_request_server(Vector<ByteString> const& certificates);
+    ErrorOr<void> launch_request_server();
     ErrorOr<void> launch_image_decoder();
     ErrorOr<void> launch_image_decoder();
     ErrorOr<NonnullRefPtr<WebView::WebContentClient>> launch_web_content(WebViewBridge&);
     ErrorOr<NonnullRefPtr<WebView::WebContentClient>> launch_web_content(WebViewBridge&);
     ErrorOr<IPC::File> launch_web_worker();
     ErrorOr<IPC::File> launch_web_worker();

+ 2 - 11
Ladybird/AppKit/Application/ApplicationDelegate.h

@@ -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
  * SPDX-License-Identifier: BSD-2-Clause
  */
  */
@@ -8,8 +8,6 @@
 
 
 #include <AK/Optional.h>
 #include <AK/Optional.h>
 #include <AK/StringView.h>
 #include <AK/StringView.h>
-#include <AK/Vector.h>
-#include <Ladybird/Types.h>
 #include <LibURL/URL.h>
 #include <LibURL/URL.h>
 #include <LibWeb/CSS/PreferredColorScheme.h>
 #include <LibWeb/CSS/PreferredColorScheme.h>
 #include <LibWeb/CSS/PreferredContrast.h>
 #include <LibWeb/CSS/PreferredContrast.h>
@@ -24,12 +22,7 @@
 
 
 @interface ApplicationDelegate : NSObject <NSApplicationDelegate>
 @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
 - (nonnull TabController*)createNewTab:(Optional<URL::URL> const&)url
                                fromTab:(nullable Tab*)tab
                                fromTab:(nullable Tab*)tab
@@ -46,8 +39,6 @@
 - (void)removeTab:(nonnull TabController*)controller;
 - (void)removeTab:(nonnull TabController*)controller;
 
 
 - (WebView::CookieJar&)cookieJar;
 - (WebView::CookieJar&)cookieJar;
-- (Ladybird::WebContentOptions const&)webContentOptions;
-- (Optional<StringView> const&)webdriverContentIPCPath;
 - (Web::CSS::PreferredColorScheme)preferredColorScheme;
 - (Web::CSS::PreferredColorScheme)preferredColorScheme;
 - (Web::CSS::PreferredContrast)preferredContrast;
 - (Web::CSS::PreferredContrast)preferredContrast;
 - (Web::CSS::PreferredMotion)preferredMotion;
 - (Web::CSS::PreferredMotion)preferredMotion;

+ 5 - 40
Ladybird/AppKit/Application/ApplicationDelegate.mm

@@ -4,6 +4,7 @@
  * SPDX-License-Identifier: BSD-2-Clause
  * SPDX-License-Identifier: BSD-2-Clause
  */
  */
 
 
+#include <LibWebView/Application.h>
 #include <LibWebView/SearchEngine.h>
 #include <LibWebView/SearchEngine.h>
 
 
 #import <Application/ApplicationDelegate.h>
 #import <Application/ApplicationDelegate.h>
@@ -29,23 +30,15 @@
 
 
 @interface ApplicationDelegate () <TaskManagerDelegate>
 @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.
     // This will always be populated, but we cannot have a non-default constructible instance variable.
     OwnPtr<WebView::CookieJar> m_cookie_jar;
     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::PreferredColorScheme m_preferred_color_scheme;
     Web::CSS::PreferredContrast m_preferred_contrast;
     Web::CSS::PreferredContrast m_preferred_contrast;
     Web::CSS::PreferredMotion m_preferred_motion;
     Web::CSS::PreferredMotion m_preferred_motion;
     ByteString m_navigator_compatibility_mode;
     ByteString m_navigator_compatibility_mode;
 
 
     WebView::SearchEngine m_search_engine;
     WebView::SearchEngine m_search_engine;
-
-    BOOL m_allow_popups;
 }
 }
 
 
 @property (nonatomic, strong) NSMutableArray<TabController*>* managed_tabs;
 @property (nonatomic, strong) NSMutableArray<TabController*>* managed_tabs;
@@ -68,12 +61,7 @@
 
 
 @implementation ApplicationDelegate
 @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]) {
     if (self = [super init]) {
         [NSApp setMainMenu:[[NSMenu alloc] init]];
         [NSApp setMainMenu:[[NSMenu alloc] init]];
@@ -91,25 +79,14 @@
 
 
         self.managed_tabs = [[NSMutableArray alloc] init];
         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_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_color_scheme = Web::CSS::PreferredColorScheme::Auto;
         m_preferred_contrast = Web::CSS::PreferredContrast::Auto;
         m_preferred_contrast = Web::CSS::PreferredContrast::Auto;
         m_preferred_motion = Web::CSS::PreferredMotion::Auto;
         m_preferred_motion = Web::CSS::PreferredMotion::Auto;
         m_navigator_compatibility_mode = "chrome";
         m_navigator_compatibility_mode = "chrome";
         m_search_engine = WebView::default_search_engine();
         m_search_engine = WebView::default_search_engine();
 
 
-        m_allow_popups = allow_popups;
-
         // Reduce the tooltip delay, as the default delay feels quite long.
         // Reduce the tooltip delay, as the default delay feels quite long.
         [[NSUserDefaults standardUserDefaults] setObject:@100 forKey:@"NSInitialToolTipDelay"];
         [[NSUserDefaults standardUserDefaults] setObject:@100 forKey:@"NSInitialToolTipDelay"];
     }
     }
@@ -124,7 +101,7 @@
                    activateTab:(Web::HTML::ActivateTab)activate_tab
                    activateTab:(Web::HTML::ActivateTab)activate_tab
 {
 {
     auto* controller = [self createNewTab:activate_tab fromTab: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;
     return controller;
 }
 }
@@ -166,16 +143,6 @@
     return *m_cookie_jar;
     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
 - (Web::CSS::PreferredColorScheme)preferredColorScheme
 {
 {
     return m_preferred_color_scheme;
     return m_preferred_color_scheme;
@@ -213,7 +180,7 @@
 - (nonnull TabController*)createNewTab:(Web::HTML::ActivateTab)activate_tab
 - (nonnull TabController*)createNewTab:(Web::HTML::ActivateTab)activate_tab
                                fromTab:(nullable Tab*)tab
                                fromTab:(nullable Tab*)tab
 {
 {
-    auto* controller = [[TabController alloc] init:!m_allow_popups];
+    auto* controller = [[TabController alloc] init];
     [controller showWindow:nil];
     [controller showWindow:nil];
 
 
     if (tab) {
     if (tab) {
@@ -740,7 +707,7 @@
 {
 {
     Tab* tab = nil;
     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 activate_tab = tab == nil ? Web::HTML::ActivateTab::Yes : Web::HTML::ActivateTab::No;
 
 
         auto* controller = [self createNewTab:url
         auto* controller = [self createNewTab:url
@@ -749,8 +716,6 @@
 
 
         tab = (Tab*)[controller window];
         tab = (Tab*)[controller window];
     }
     }
-
-    m_initial_urls.clear();
 }
 }
 
 
 - (void)applicationWillTerminate:(NSNotification*)notification
 - (void)applicationWillTerminate:(NSNotification*)notification

+ 1 - 1
Ladybird/AppKit/UI/LadybirdWebView.mm

@@ -104,7 +104,7 @@ struct HideCursor {
         // This returns device pixel ratio of the screen the window is opened in
         // This returns device pixel ratio of the screen the window is opened in
         auto device_pixel_ratio = [[NSScreen mainScreen] backingScaleFactor];
         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];
         [self setWebViewCallbacks];
 
 
         m_web_view_bridge->initialize_client();
         m_web_view_bridge->initialize_client();

+ 7 - 9
Ladybird/AppKit/UI/LadybirdWebViewBridge.cpp

@@ -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
  * SPDX-License-Identifier: BSD-2-Clause
  */
  */
 
 
 #include <Ladybird/HelperProcess.h>
 #include <Ladybird/HelperProcess.h>
-#include <Ladybird/Types.h>
 #include <Ladybird/Utilities.h>
 #include <Ladybird/Utilities.h>
 #include <LibGfx/Font/FontDatabase.h>
 #include <LibGfx/Font/FontDatabase.h>
 #include <LibGfx/Rect.h>
 #include <LibGfx/Rect.h>
 #include <LibIPC/File.h>
 #include <LibIPC/File.h>
 #include <LibWeb/Crypto/Crypto.h>
 #include <LibWeb/Crypto/Crypto.h>
+#include <LibWebView/Application.h>
 #include <UI/LadybirdWebViewBridge.h>
 #include <UI/LadybirdWebViewBridge.h>
 
 
 #import <UI/Palette.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>();
     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_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_color_scheme(preferred_color_scheme)
     , m_preferred_contrast(preferred_contrast)
     , m_preferred_contrast(preferred_contrast)
     , m_preferred_motion(preferred_motion)
     , 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);
         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);
     }
     }
 }
 }
 
 

+ 3 - 9
Ladybird/AppKit/UI/LadybirdWebViewBridge.h

@@ -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
  * SPDX-License-Identifier: BSD-2-Clause
  */
  */
@@ -7,7 +7,6 @@
 #pragma once
 #pragma once
 
 
 #include <AK/Vector.h>
 #include <AK/Vector.h>
-#include <Ladybird/Types.h>
 #include <LibGfx/Point.h>
 #include <LibGfx/Point.h>
 #include <LibGfx/Rect.h>
 #include <LibGfx/Rect.h>
 #include <LibGfx/Size.h>
 #include <LibGfx/Size.h>
@@ -22,13 +21,11 @@ namespace Ladybird {
 
 
 class WebViewBridge final : public WebView::ViewImplementation {
 class WebViewBridge final : public WebView::ViewImplementation {
 public:
 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 ~WebViewBridge() override;
 
 
     virtual void initialize_client(CreateNewClient = CreateNewClient::Yes) 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; }
     float device_pixel_ratio() const { return m_device_pixel_ratio; }
     void set_device_pixel_ratio(float 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; }
     float inverse_device_pixel_ratio() const { return 1.0f / m_device_pixel_ratio; }
@@ -59,7 +56,7 @@ public:
     Function<void()> on_zoom_level_changed;
     Function<void()> on_zoom_level_changed;
 
 
 private:
 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 void update_zoom() override;
     virtual Web::DevicePixelSize viewport_size() const override;
     virtual Web::DevicePixelSize viewport_size() const override;
@@ -69,9 +66,6 @@ private:
     Vector<Web::DevicePixelRect> m_screen_rects;
     Vector<Web::DevicePixelRect> m_screen_rects;
     Gfx::IntSize m_viewport_size;
     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::PreferredColorScheme m_preferred_color_scheme { Web::CSS::PreferredColorScheme::Auto };
     Web::CSS::PreferredContrast m_preferred_contrast { Web::CSS::PreferredContrast::Auto };
     Web::CSS::PreferredContrast m_preferred_contrast { Web::CSS::PreferredContrast::Auto };
     Web::CSS::PreferredMotion m_preferred_motion { Web::CSS::PreferredMotion::Auto };
     Web::CSS::PreferredMotion m_preferred_motion { Web::CSS::PreferredMotion::Auto };

+ 2 - 2
Ladybird/AppKit/UI/TabController.h

@@ -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
  * SPDX-License-Identifier: BSD-2-Clause
  */
  */
@@ -22,7 +22,7 @@ struct TabSettings {
 
 
 @interface TabController : NSWindowController <NSWindowDelegate>
 @interface TabController : NSWindowController <NSWindowDelegate>
 
 
-- (instancetype)init:(BOOL)block_popups;
+- (instancetype)init;
 
 
 - (void)loadURL:(URL::URL const&)url;
 - (void)loadURL:(URL::URL const&)url;
 - (void)loadHTML:(StringView)html url:(URL::URL const&)url;
 - (void)loadHTML:(StringView)html url:(URL::URL const&)url;

+ 4 - 3
Ladybird/AppKit/UI/TabController.mm

@@ -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
  * SPDX-License-Identifier: BSD-2-Clause
  */
  */
 
 
 #include <LibWeb/Loader/UserAgent.h>
 #include <LibWeb/Loader/UserAgent.h>
+#include <LibWebView/Application.h>
 #include <LibWebView/SearchEngine.h>
 #include <LibWebView/SearchEngine.h>
 #include <LibWebView/URL.h>
 #include <LibWebView/URL.h>
 #include <LibWebView/UserAgent.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 new_tab_toolbar_item = _new_tab_toolbar_item;
 @synthesize tab_overview_toolbar_item = _tab_overview_toolbar_item;
 @synthesize tab_overview_toolbar_item = _tab_overview_toolbar_item;
 
 
-- (instancetype)init:(BOOL)block_popups
+- (instancetype)init
 {
 {
     if (self = [super init]) {
     if (self = [super init]) {
         self.toolbar = [[NSToolbar alloc] initWithIdentifier:TOOLBAR_IDENTIFIER];
         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 setAllowsUserCustomization:NO];
         [self.toolbar setSizeMode:NSToolbarSizeModeRegular];
         [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_back = false;
         m_can_navigate_forward = false;
         m_can_navigate_forward = false;
     }
     }

+ 19 - 78
Ladybird/AppKit/main.mm

@@ -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
  * SPDX-License-Identifier: BSD-2-Clause
  */
  */
@@ -7,9 +7,7 @@
 #include <AK/Enumerate.h>
 #include <AK/Enumerate.h>
 #include <Ladybird/DefaultSettings.h>
 #include <Ladybird/DefaultSettings.h>
 #include <Ladybird/MachPortServer.h>
 #include <Ladybird/MachPortServer.h>
-#include <Ladybird/Types.h>
 #include <Ladybird/Utilities.h>
 #include <Ladybird/Utilities.h>
-#include <LibCore/ArgsParser.h>
 #include <LibGfx/Font/FontDatabase.h>
 #include <LibGfx/Font/FontDatabase.h>
 #include <LibMain/Main.h>
 #include <LibMain/Main.h>
 #include <LibWebView/Application.h>
 #include <LibWebView/Application.h>
@@ -30,33 +28,10 @@
 #    error "This project requires ARC"
 #    error "This project requires ARC"
 #endif
 #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];
     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)) {
     for (auto [i, url] : enumerate(urls)) {
         auto activate_tab = i == 0 ? Web::HTML::ActivateTab::Yes : Web::HTML::ActivateTab::No;
         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];
     Application* application = [Application sharedApplication];
 
 
     Core::EventLoopManager::install(*new Ladybird::CFEventLoopManager);
     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();
     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());
     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) {
     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) {
     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>();
     auto mach_port_server = make<Ladybird::MachPortServer>();
     set_mach_server_name(mach_port_server->server_port_name());
     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) {
     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())
         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));
     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
     // 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]);
     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];
     [NSApp setDelegate:delegate];
 
 
-    return web_view_app.exec();
+    return WebView::Application::the().execute();
 }
 }

+ 0 - 1
Ladybird/CMakeLists.txt

@@ -6,7 +6,6 @@ set(LADYBIRD_SOURCES
 )
 )
 set(LADYBIRD_HEADERS
 set(LADYBIRD_HEADERS
     HelperProcess.h
     HelperProcess.h
-    Types.h
     Utilities.h
     Utilities.h
 )
 )
 
 

+ 19 - 18
Ladybird/HelperProcess.cpp

@@ -15,10 +15,10 @@ static ErrorOr<NonnullRefPtr<ClientType>> launch_server_process(
     StringView server_name,
     StringView server_name,
     ReadonlySpan<ByteString> candidate_server_paths,
     ReadonlySpan<ByteString> candidate_server_paths,
     Vector<ByteString> arguments,
     Vector<ByteString> arguments,
-    Ladybird::EnableCallgrindProfiling enable_callgrind_profiling,
+    WebView::EnableCallgrindProfiling enable_callgrind_profiling,
     ClientArguments&&... client_arguments)
     ClientArguments&&... client_arguments)
 {
 {
-    if (enable_callgrind_profiling == Ladybird::EnableCallgrindProfiling::Yes) {
+    if (enable_callgrind_profiling == WebView::EnableCallgrindProfiling::Yes) {
         arguments.prepend({
         arguments.prepend({
             "--tool=callgrind"sv,
             "--tool=callgrind"sv,
             "--instr-atstart=no"sv,
             "--instr-atstart=no"sv,
@@ -29,7 +29,7 @@ static ErrorOr<NonnullRefPtr<ClientType>> launch_server_process(
     for (auto [i, path] : enumerate(candidate_server_paths)) {
     for (auto [i, path] : enumerate(candidate_server_paths)) {
         Core::ProcessSpawnOptions options { .name = server_name, .arguments = arguments };
         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.executable = "valgrind"sv;
             options.search_for_executable_in_path = true;
             options.search_for_executable_in_path = true;
             arguments[2] = path;
             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) });
             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();
                 dbgln("\033[1;45mLaunched {} process under callgrind!\033[0m", server_name);
                 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");
                 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(
 ErrorOr<NonnullRefPtr<WebView::WebContentClient>> launch_web_content_process(
     WebView::ViewImplementation& view,
     WebView::ViewImplementation& view,
     ReadonlySpan<ByteString> candidate_web_content_paths,
     ReadonlySpan<ByteString> candidate_web_content_paths,
-    Ladybird::WebContentOptions const& web_content_options,
     IPC::File image_decoder_socket,
     IPC::File image_decoder_socket,
     Optional<IPC::File> request_server_socket)
     Optional<IPC::File> request_server_socket)
 {
 {
+    auto const& web_content_options = WebView::Application::web_content_options();
+
     Vector<ByteString> arguments {
     Vector<ByteString> arguments {
         "--command-line"sv,
         "--command-line"sv,
         web_content_options.command_line.to_byte_string(),
         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("--config-path"sv);
         arguments.append(web_content_options.config_path.value());
         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);
         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);
         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);
         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);
         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);
         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);
         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);
         arguments.append("--expose-internals-object"sv);
     if (auto server = mach_server_name(); server.has_value()) {
     if (auto server = mach_server_name(); server.has_value()) {
         arguments.append("--mach-server-name"sv);
         arguments.append("--mach-server-name"sv);
@@ -121,7 +122,7 @@ ErrorOr<NonnullRefPtr<ImageDecoderClient::Client>> launch_image_decoder_process(
         arguments.append(server.value());
         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)
 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("--request-server-socket"sv);
         arguments.append(ByteString::number(socket.fd()));
         arguments.append(ByteString::number(socket.fd()));
         arguments.append("--use-lagom-networking"sv);
         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;
     Vector<ByteString> arguments;
 
 
@@ -147,7 +148,7 @@ ErrorOr<NonnullRefPtr<Protocol::RequestClient>> launch_request_server_process(Re
         arguments.append(serenity_resource_root);
         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));
         arguments.append(ByteString::formatted("--certificate={}", certificate));
 
 
     if (auto server = mach_server_name(); server.has_value()) {
     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());
         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)
 ErrorOr<IPC::File> connect_new_request_server_client(Protocol::RequestClient& client)

+ 1 - 3
Ladybird/HelperProcess.h

@@ -6,7 +6,6 @@
 
 
 #pragma once
 #pragma once
 
 
-#include "Types.h"
 #include <AK/Error.h>
 #include <AK/Error.h>
 #include <AK/Optional.h>
 #include <AK/Optional.h>
 #include <AK/Span.h>
 #include <AK/Span.h>
@@ -20,13 +19,12 @@
 ErrorOr<NonnullRefPtr<WebView::WebContentClient>> launch_web_content_process(
 ErrorOr<NonnullRefPtr<WebView::WebContentClient>> launch_web_content_process(
     WebView::ViewImplementation& view,
     WebView::ViewImplementation& view,
     ReadonlySpan<ByteString> candidate_web_content_paths,
     ReadonlySpan<ByteString> candidate_web_content_paths,
-    Ladybird::WebContentOptions const&,
     IPC::File image_decoder_socket,
     IPC::File image_decoder_socket,
     Optional<IPC::File> request_server_socket = {});
     Optional<IPC::File> request_server_socket = {});
 
 
 ErrorOr<NonnullRefPtr<ImageDecoderClient::Client>> launch_image_decoder_process(ReadonlySpan<ByteString> candidate_image_decoder_paths);
 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<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_request_server_client(Protocol::RequestClient&);
 ErrorOr<IPC::File> connect_new_image_decoder_client(ImageDecoderClient::Client&);
 ErrorOr<IPC::File> connect_new_image_decoder_client(ImageDecoderClient::Client&);

+ 22 - 9
Ladybird/Qt/Application.cpp

@@ -4,21 +4,34 @@
  * SPDX-License-Identifier: BSD-2-Clause
  * SPDX-License-Identifier: BSD-2-Clause
  */
  */
 
 
-#include "Application.h"
-#include "StringUtils.h"
-#include "TaskManagerWindow.h"
 #include <Ladybird/HelperProcess.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 <Ladybird/Utilities.h>
+#include <LibCore/ArgsParser.h>
 #include <LibWebView/URL.h>
 #include <LibWebView/URL.h>
 #include <QFileOpenEvent>
 #include <QFileOpenEvent>
 
 
 namespace Ladybird {
 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()
 Application::~Application()
 {
 {
     close_task_manager_window();
     close_task_manager_window();
@@ -77,10 +90,10 @@ ErrorOr<void> Application::initialize_image_decoder()
     return {};
     return {};
 }
 }
 
 
-void Application::show_task_manager_window(WebContentOptions const& web_content_options)
+void Application::show_task_manager_window()
 {
 {
     if (!m_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->show();
     m_task_manager_window->activateWindow();
     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);
     set_active_window(*window);
     window->show();
     window->show();
     if (initial_urls.is_empty()) {
     if (initial_urls.is_empty()) {

+ 12 - 4
Ladybird/Qt/Application.h

@@ -12,15 +12,18 @@
 #include <LibImageDecoderClient/Client.h>
 #include <LibImageDecoderClient/Client.h>
 #include <LibProtocol/RequestClient.h>
 #include <LibProtocol/RequestClient.h>
 #include <LibURL/URL.h>
 #include <LibURL/URL.h>
+#include <LibWebView/Application.h>
 #include <QApplication>
 #include <QApplication>
 
 
 namespace Ladybird {
 namespace Ladybird {
 
 
-class Application : public QApplication {
+class Application
+    : public QApplication
+    , public WebView::Application {
     Q_OBJECT
     Q_OBJECT
+    WEB_VIEW_APPLICATION(Application)
 
 
 public:
 public:
-    Application(int& argc, char** argv);
     virtual ~Application() override;
     virtual ~Application() override;
 
 
     virtual bool event(QEvent* event) override;
     virtual bool event(QEvent* event) override;
@@ -31,15 +34,20 @@ public:
     NonnullRefPtr<ImageDecoderClient::Client> image_decoder_client() const { return *m_image_decoder_client; }
     NonnullRefPtr<ImageDecoderClient::Client> image_decoder_client() const { return *m_image_decoder_client; }
     ErrorOr<void> initialize_image_decoder();
     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();
     void close_task_manager_window();
 
 
     BrowserWindow& active_window() { return *m_active_window; }
     BrowserWindow& active_window() { return *m_active_window; }
     void set_active_window(BrowserWindow& w) { m_active_window = &w; }
     void set_active_window(BrowserWindow& w) { m_active_window = &w; }
 
 
 private:
 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 };
     TaskManagerWindow* m_task_manager_window { nullptr };
     BrowserWindow* m_active_window { nullptr };
     BrowserWindow* m_active_window { nullptr };
 
 

+ 10 - 16
Ladybird/Qt/BrowserWindow.cpp

@@ -23,6 +23,7 @@
 #include <LibWeb/CSS/PreferredContrast.h>
 #include <LibWeb/CSS/PreferredContrast.h>
 #include <LibWeb/CSS/PreferredMotion.h>
 #include <LibWeb/CSS/PreferredMotion.h>
 #include <LibWeb/Loader/UserAgent.h>
 #include <LibWeb/Loader/UserAgent.h>
+#include <LibWebView/Application.h>
 #include <LibWebView/CookieJar.h>
 #include <LibWebView/CookieJar.h>
 #include <LibWebView/UserAgent.h>
 #include <LibWebView/UserAgent.h>
 #include <QAction>
 #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_tabs_container(new TabWidget(this))
     , m_new_tab_button_toolbar(new QToolBar("New Tab", m_tabs_container))
     , m_new_tab_button_toolbar(new QToolBar("New Tab", m_tabs_container))
     , m_cookie_jar(cookie_jar)
     , 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)
     , m_is_popup_window(is_popup_window)
 {
 {
     setWindowIcon(app_icon());
     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") });
     task_manager_action->setShortcuts({ QKeySequence("Ctrl+Shift+M") });
     inspect_menu->addAction(task_manager_action);
     inspect_menu->addAction(task_manager_action);
     QObject::connect(task_manager_action, &QAction::triggered, this, [&] {
     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");
     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 = new QAction("Block Pop-ups", this);
     m_block_pop_ups_action->setCheckable(true);
     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);
     debug_menu->addAction(m_block_pop_ups_action);
     QObject::connect(m_block_pop_ups_action, &QAction::triggered, this, [this] {
     QObject::connect(m_block_pop_ups_action, &QAction::triggered, this, [this] {
         bool state = m_block_pop_ups_action->isChecked();
         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();
         tab.focus_location_editor();
     });
     });
     QObject::connect(m_new_window_action, &QAction::triggered, this, [this] {
     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(open_file_action, &QAction::triggered, this, &BrowserWindow::open_file);
     QObject::connect(settings_action, &QAction::triggered, this, [this] {
     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) {
     if (parent_tab) {
         new_child_tab(Web::HTML::ActivateTab::Yes, *parent_tab, AK::move(page_index));
         new_child_tab(Web::HTML::ActivateTab::Yes, *parent_tab, AK::move(page_index));
     } else {
     } 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())
     if (!page_index.has_value())
         return create_new_tab(activate_tab);
         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
     // FIXME: Merge with other overload
     if (m_current_tab == nullptr) {
     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)
 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) {
     if (m_current_tab == nullptr) {
         set_current_tab(tab);
         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) {
     tab->view().on_new_web_view = [this, tab](auto activate_tab, Web::HTML::WebViewHints hints, Optional<u64> page_index) {
         if (hints.popup) {
         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);
             window.set_window_rect(hints.screen_x, hints.screen_y, hints.width, hints.height);
             return window.current_tab()->view().handle();
             return window.current_tab()->view().handle();
         }
         }

+ 1 - 6
Ladybird/Qt/BrowserWindow.h

@@ -9,7 +9,6 @@
 
 
 #include "Tab.h"
 #include "Tab.h"
 #include <Ladybird/Qt/FindInPageWidget.h>
 #include <Ladybird/Qt/FindInPageWidget.h>
-#include <Ladybird/Types.h>
 #include <LibCore/Forward.h>
 #include <LibCore/Forward.h>
 #include <LibWeb/HTML/ActivateTab.h>
 #include <LibWeb/HTML/ActivateTab.h>
 #include <LibWeb/HTML/AudioPlayState.h>
 #include <LibWeb/HTML/AudioPlayState.h>
@@ -36,7 +35,7 @@ public:
         Yes,
         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(); }
     WebContentView& view() const { return m_current_tab->view(); }
 
 
@@ -215,10 +214,6 @@ private:
 
 
     WebView::CookieJar& m_cookie_jar;
     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 };
     IsPopupWindow m_is_popup_window { IsPopupWindow::No };
 };
 };
 
 

+ 1 - 1
Ladybird/Qt/InspectorWidget.cpp

@@ -22,7 +22,7 @@ extern bool is_using_dark_system_theme(QWidget&);
 InspectorWidget::InspectorWidget(QWidget* tab, WebContentView& content_view)
 InspectorWidget::InspectorWidget(QWidget* tab, WebContentView& content_view)
     : QWidget(tab, Qt::Window)
     : 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))
     if (is_using_dark_system_theme(*this))
         m_inspector_view->update_palette(WebContentView::PaletteMode::Dark);
         m_inspector_view->update_palette(WebContentView::PaletteMode::Dark);

+ 2 - 2
Ladybird/Qt/Tab.cpp

@@ -47,7 +47,7 @@ static QIcon default_favicon()
     return icon;
     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)
     : QWidget(window)
     , m_window(window)
     , m_window(window)
 {
 {
@@ -55,7 +55,7 @@ Tab::Tab(BrowserWindow* window, WebContentOptions const& web_content_options, St
     m_layout->setSpacing(0);
     m_layout->setSpacing(0);
     m_layout->setContentsMargins(0, 0, 0, 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 = new FindInPageWidget(this, m_view);
     m_find_in_page->setVisible(false);
     m_find_in_page->setVisible(false);
     m_toolbar = new QToolBar(this);
     m_toolbar = new QToolBar(this);

+ 1 - 1
Ladybird/Qt/Tab.h

@@ -29,7 +29,7 @@ class Tab final : public QWidget {
     Q_OBJECT
     Q_OBJECT
 
 
 public:
 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;
     virtual ~Tab() override;
 
 
     WebContentView& view() { return *m_view; }
     WebContentView& view() { return *m_view; }

+ 2 - 2
Ladybird/Qt/TaskManagerWindow.cpp

@@ -10,9 +10,9 @@
 
 
 namespace Ladybird {
 namespace Ladybird {
 
 
-TaskManagerWindow::TaskManagerWindow(QWidget* parent, WebContentOptions const& web_content_options)
+TaskManagerWindow::TaskManagerWindow(QWidget* parent)
     : QWidget(parent, Qt::WindowFlags(Qt::WindowType::Window))
     : 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);
     setLayout(new QVBoxLayout);
     layout()->addWidget(m_web_view);
     layout()->addWidget(m_web_view);

+ 1 - 1
Ladybird/Qt/TaskManagerWindow.h

@@ -16,7 +16,7 @@ class TaskManagerWindow : public QWidget {
     Q_OBJECT
     Q_OBJECT
 
 
 public:
 public:
-    TaskManagerWindow(QWidget* parent, WebContentOptions const&);
+    explicit TaskManagerWindow(QWidget* parent);
 
 
 private:
 private:
     virtual void showEvent(QShowEvent*) override;
     virtual void showEvent(QShowEvent*) override;

+ 7 - 8
Ladybird/Qt/WebContentView.cpp

@@ -30,6 +30,7 @@
 #include <LibWeb/UIEvents/KeyCode.h>
 #include <LibWeb/UIEvents/KeyCode.h>
 #include <LibWeb/UIEvents/MouseButton.h>
 #include <LibWeb/UIEvents/MouseButton.h>
 #include <LibWeb/Worker/WebWorkerClient.h>
 #include <LibWeb/Worker/WebWorkerClient.h>
+#include <LibWebView/Application.h>
 #include <LibWebView/WebContentClient.h>
 #include <LibWebView/WebContentClient.h>
 #include <QApplication>
 #include <QApplication>
 #include <QCursor>
 #include <QCursor>
@@ -50,10 +51,8 @@ namespace Ladybird {
 
 
 bool is_using_dark_system_theme(QWidget&);
 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)
     : QAbstractScrollArea(window)
-    , m_web_content_options(web_content_options)
-    , m_webdriver_content_ipc_path(webdriver_content_ipc_path)
 {
 {
     setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
     setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
     setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
     setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
@@ -133,7 +132,7 @@ WebContentView::WebContentView(QWidget* window, WebContentOptions const& web_con
 
 
     on_request_worker_agent = [&]() {
     on_request_worker_agent = [&]() {
         RefPtr<Protocol::RequestClient> request_server_client {};
         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;
             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));
         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 = {};
         m_client_state = {};
 
 
         Optional<IPC::File> request_server_socket;
         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;
             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
             // 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 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 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;
         m_client_state.client = new_client;
     } else {
     } else {
@@ -607,8 +606,8 @@ void WebContentView::initialize_client(WebView::ViewImplementation::CreateNewCli
 
 
     update_screen_rects();
     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)
 void WebContentView::update_cursor(Gfx::StandardCursor cursor)

+ 1 - 7
Ladybird/Qt/WebContentView.h

@@ -11,7 +11,6 @@
 #include <AK/Function.h>
 #include <AK/Function.h>
 #include <AK/HashMap.h>
 #include <AK/HashMap.h>
 #include <AK/OwnPtr.h>
 #include <AK/OwnPtr.h>
-#include <Ladybird/Types.h>
 #include <LibGfx/Forward.h>
 #include <LibGfx/Forward.h>
 #include <LibGfx/Rect.h>
 #include <LibGfx/Rect.h>
 #include <LibGfx/StandardCursor.h>
 #include <LibGfx/StandardCursor.h>
@@ -46,7 +45,7 @@ class WebContentView final
     , public WebView::ViewImplementation {
     , public WebView::ViewImplementation {
     Q_OBJECT
     Q_OBJECT
 public:
 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;
     virtual ~WebContentView() override;
 
 
     Function<String(const URL::URL&, Web::HTML::ActivateTab)> on_tab_open_request;
     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;
     QPoint map_point_to_global_position(Gfx::IntPoint) const;
 
 
-    WebContentOptions const& web_content_options() const { return m_web_content_options; }
-
 signals:
 signals:
     void urls_dropped(QList<QUrl> const&);
     void urls_dropped(QList<QUrl> const&);
 
 
@@ -113,9 +110,6 @@ private:
     bool m_should_show_line_box_borders { false };
     bool m_should_show_line_box_borders { false };
 
 
     Gfx::IntSize m_viewport_size;
     Gfx::IntSize m_viewport_size;
-
-    WebContentOptions m_web_content_options;
-    StringView m_webdriver_content_ipc_path;
 };
 };
 
 
 }
 }

+ 25 - 80
Ladybird/Qt/main.cpp

@@ -62,72 +62,32 @@ static ErrorOr<void> handle_attached_debugger()
     return {};
     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)
 ErrorOr<int> serenity_main(Main::Arguments arguments)
 {
 {
     AK::set_rich_debug_enabled(true);
     AK::set_rich_debug_enabled(true);
 
 
-    Ladybird::Application app(arguments.argc, arguments.argv);
-
     Core::EventLoopManager::install(*new Ladybird::EventLoopManagerQt);
     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());
     TRY(handle_attached_debugger());
 
 
     platform_init();
     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());
     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) {
         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);
             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();
         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);
         window.view().load(file_url);
     };
     };
 
 
 #if defined(AK_OS_MACOS)
 #if defined(AK_OS_MACOS)
     auto mach_port_server = make<Ladybird::MachPortServer>();
     auto mach_port_server = make<Ladybird::MachPortServer>();
     set_mach_server_name(mach_port_server->server_port_name());
     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) {
     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())
         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());
     copy_default_config_files(Ladybird::Settings::the()->directory());
 
 
     RefPtr<WebView::Database> database;
     RefPtr<WebView::Database> database;
-    if (!disable_sql_database)
+    if (app->chrome_options().disable_sql_database == WebView::DisableSQLDatabase::No)
         database = TRY(WebView::Database::create());
         database = TRY(WebView::Database::create());
 
 
     auto cookie_jar = database ? TRY(WebView::CookieJar::create(*database)) : WebView::CookieJar::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
     // 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 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) {
     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");
     window.setWindowTitle("Ladybird");
 
 
     if (Ladybird::Settings::the()->is_maximized()) {
     if (Ladybird::Settings::the()->is_maximized()) {
@@ -203,5 +148,5 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
 
 
     window.show();
     window.show();
 
 
-    return webview_app.exec();
+    return app->execute();
 }
 }

+ 67 - 2
Userland/Libraries/LibWebView/Application.cpp

@@ -5,15 +5,17 @@
  */
  */
 
 
 #include <AK/Debug.h>
 #include <AK/Debug.h>
+#include <LibCore/ArgsParser.h>
 #include <LibImageDecoderClient/Client.h>
 #include <LibImageDecoderClient/Client.h>
 #include <LibWebView/Application.h>
 #include <LibWebView/Application.h>
+#include <LibWebView/URL.h>
 #include <LibWebView/WebContentClient.h>
 #include <LibWebView/WebContentClient.h>
 
 
 namespace WebView {
 namespace WebView {
 
 
 Application* Application::s_the = nullptr;
 Application* Application::s_the = nullptr;
 
 
-Application::Application(int, char**)
+Application::Application()
 {
 {
     VERIFY(!s_the);
     VERIFY(!s_the);
     s_the = this;
     s_the = this;
@@ -28,7 +30,70 @@ Application::~Application()
     s_the = nullptr;
     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();
     int ret = m_event_loop.exec();
     m_in_shutdown = true;
     m_in_shutdown = true;

+ 36 - 2
Userland/Libraries/LibWebView/Application.h

@@ -6,7 +6,11 @@
 
 
 #pragma once
 #pragma once
 
 
+#include <AK/Badge.h>
 #include <LibCore/EventLoop.h>
 #include <LibCore/EventLoop.h>
+#include <LibMain/Main.h>
+#include <LibURL/URL.h>
+#include <LibWebView/Options.h>
 #include <LibWebView/Process.h>
 #include <LibWebView/Process.h>
 #include <LibWebView/ProcessManager.h>
 #include <LibWebView/ProcessManager.h>
 
 
@@ -22,13 +26,15 @@ class Application {
     AK_MAKE_NONCOPYABLE(Application);
     AK_MAKE_NONCOPYABLE(Application);
 
 
 public:
 public:
-    Application(int argc, char** argv);
     virtual ~Application();
     virtual ~Application();
 
 
-    int exec();
+    int execute();
 
 
     static Application& the() { return *s_the; }
     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; }
     Core::EventLoop& event_loop() { return m_event_loop; }
 
 
     void add_child_process(Process&&);
     void add_child_process(Process&&);
@@ -44,14 +50,42 @@ public:
     String generate_process_statistics_html();
     String generate_process_statistics_html();
 
 
 protected:
 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 process_did_exit(Process&&);
 
 
+    virtual void create_platform_arguments(Core::ArgsParser&) { }
+    virtual void create_platform_options(ChromeOptions&, WebContentOptions&) { }
+
 private:
 private:
+    void initialize(Main::Arguments const& arguments, URL::URL new_tab_page_url);
+
     static Application* s_the;
     static Application* s_the;
 
 
+    ChromeOptions m_chrome_options;
+    WebContentOptions m_web_content_options;
+
     Core::EventLoop m_event_loop;
     Core::EventLoop m_event_loop;
     ProcessManager m_process_manager;
     ProcessManager m_process_manager;
     bool m_in_shutdown { false };
     bool m_in_shutdown { false };
 } SWIFT_IMMORTAL_REFERENCE;
 } 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&);

+ 7 - 5
Userland/Libraries/LibWebView/ChromeProcess.cpp

@@ -9,7 +9,9 @@
 #include <LibCore/StandardPaths.h>
 #include <LibCore/StandardPaths.h>
 #include <LibCore/System.h>
 #include <LibCore/System.h>
 #include <LibIPC/ConnectionToServer.h>
 #include <LibIPC/ConnectionToServer.h>
+#include <LibWebView/Application.h>
 #include <LibWebView/ChromeProcess.h>
 #include <LibWebView/ChromeProcess.h>
+#include <LibWebView/URL.h>
 
 
 namespace WebView {
 namespace WebView {
 
 
@@ -32,7 +34,7 @@ ErrorOr<ChromeProcess> ChromeProcess::create()
     return ChromeProcess {};
     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;
     static constexpr auto process_name = "Ladybird"sv;
 
 
@@ -52,12 +54,12 @@ ErrorOr<ChromeProcess::ProcessDisposition> ChromeProcess::connect(Vector<ByteStr
     return ProcessDisposition::ContinueMainProcess;
     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 socket = TRY(Core::LocalSocket::connect(socket_path));
     auto client = UIProcessClient::construct(move(socket));
     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))
         if (!client->send_sync_but_allow_failure<Messages::UIProcessServer::CreateNewWindow>(raw_urls))
             dbgln("Failed to send CreateNewWindow message to UIProcess");
             dbgln("Failed to send CreateNewWindow message to UIProcess");
     } else {
     } else {
@@ -121,13 +123,13 @@ void UIProcessConnectionFromClient::die()
 void UIProcessConnectionFromClient::create_new_tab(Vector<ByteString> const& urls)
 void UIProcessConnectionFromClient::create_new_tab(Vector<ByteString> const& urls)
 {
 {
     if (on_new_tab)
     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)
 void UIProcessConnectionFromClient::create_new_window(Vector<ByteString> const& urls)
 {
 {
     if (on_new_window)
     if (on_new_window)
-        on_new_window(urls);
+        on_new_window(sanitize_urls(urls, Application::chrome_options().new_tab_page_url));
 }
 }
 
 
 }
 }

+ 7 - 6
Userland/Libraries/LibWebView/ChromeProcess.h

@@ -14,6 +14,7 @@
 #include <LibIPC/ConnectionFromClient.h>
 #include <LibIPC/ConnectionFromClient.h>
 #include <LibIPC/Forward.h>
 #include <LibIPC/Forward.h>
 #include <LibIPC/MultiServer.h>
 #include <LibIPC/MultiServer.h>
+#include <LibWebView/Options.h>
 #include <LibWebView/UIProcessClientEndpoint.h>
 #include <LibWebView/UIProcessClientEndpoint.h>
 #include <LibWebView/UIProcessServerEndpoint.h>
 #include <LibWebView/UIProcessServerEndpoint.h>
 
 
@@ -28,8 +29,8 @@ public:
 
 
     virtual void die() override;
     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:
 private:
     UIProcessConnectionFromClient(NonnullOwnPtr<Core::LocalSocket>, int client_id);
     UIProcessConnectionFromClient(NonnullOwnPtr<Core::LocalSocket>, int client_id);
@@ -51,15 +52,15 @@ public:
     static ErrorOr<ChromeProcess> create();
     static ErrorOr<ChromeProcess> create();
     ~ChromeProcess();
     ~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:
 private:
     ChromeProcess() = default;
     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);
     ErrorOr<void> connect_as_server(ByteString const& socket_path);
 
 
     OwnPtr<IPC::MultiServer<UIProcessConnectionFromClient>> m_server_connection;
     OwnPtr<IPC::MultiServer<UIProcessConnectionFromClient>> m_server_connection;

+ 45 - 9
Ladybird/Types.h → Userland/Libraries/LibWebView/Options.h

@@ -6,48 +6,84 @@
 
 
 #pragma once
 #pragma once
 
 
+#include <AK/ByteString.h>
+#include <AK/Optional.h>
 #include <AK/String.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 {
 enum class EnableCallgrindProfiling {
     No,
     No,
-    Yes
+    Yes,
 };
 };
 
 
 enum class IsLayoutTestMode {
 enum class IsLayoutTestMode {
     No,
     No,
-    Yes
+    Yes,
 };
 };
 
 
 enum class UseLagomNetworking {
 enum class UseLagomNetworking {
     No,
     No,
-    Yes
+    Yes,
 };
 };
 
 
 enum class WaitForDebugger {
 enum class WaitForDebugger {
     No,
     No,
-    Yes
+    Yes,
 };
 };
 
 
 enum class LogAllJSExceptions {
 enum class LogAllJSExceptions {
     No,
     No,
-    Yes
+    Yes,
 };
 };
 
 
 enum class EnableIDLTracing {
 enum class EnableIDLTracing {
     No,
     No,
-    Yes
+    Yes,
 };
 };
 
 
 enum class EnableHTTPCache {
 enum class EnableHTTPCache {
     No,
     No,
-    Yes
+    Yes,
 };
 };
 
 
 enum class ExposeInternalsObject {
 enum class ExposeInternalsObject {
     No,
     No,
-    Yes
+    Yes,
 };
 };
 
 
 struct WebContentOptions {
 struct WebContentOptions {

+ 16 - 0
Userland/Libraries/LibWebView/URL.cpp

@@ -72,6 +72,22 @@ Optional<URL::URL> sanitize_url(StringView url, Optional<StringView> search_engi
     return result;
     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)
 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());
     auto scheme = url_string.substring_view(0, url.scheme().bytes_as_string_view().length() + "://"sv.length());

+ 1 - 0
Userland/Libraries/LibWebView/URL.h

@@ -20,6 +20,7 @@ enum class AppendTLD {
     Yes,
     Yes,
 };
 };
 Optional<URL::URL> sanitize_url(StringView, Optional<StringView> search_engine = {}, AppendTLD = AppendTLD::No);
 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 {
 struct URLParts {
     StringView scheme_and_subdomain;
     StringView scheme_and_subdomain;

+ 79 - 81
Userland/Utilities/headless-browser.cpp

@@ -19,7 +19,6 @@
 #include <AK/String.h>
 #include <AK/String.h>
 #include <AK/Vector.h>
 #include <AK/Vector.h>
 #include <Ladybird/HelperProcess.h>
 #include <Ladybird/HelperProcess.h>
-#include <Ladybird/Types.h>
 #include <Ladybird/Utilities.h>
 #include <Ladybird/Utilities.h>
 #include <LibCore/ArgsParser.h>
 #include <LibCore/ArgsParser.h>
 #include <LibCore/ConfigFile.h>
 #include <LibCore/ConfigFile.h>
@@ -64,13 +63,13 @@ static StringView s_current_test_path;
 
 
 class HeadlessWebContentView final : public WebView::ViewImplementation {
 class HeadlessWebContentView final : public WebView::ViewImplementation {
 public:
 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<Protocol::RequestClient> request_client;
         RefPtr<ImageDecoderClient::Client> image_decoder_client;
         RefPtr<ImageDecoderClient::Client> image_decoder_client;
 
 
         auto request_server_paths = TRY(get_paths_for_helper_process("RequestServer"sv));
         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));
         auto image_decoder_paths = TRY(get_paths_for_helper_process("ImageDecoder"sv));
         image_decoder_client = TRY(launch_image_decoder_process(image_decoder_paths));
         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)));
         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 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 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));
         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));
         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_viewport_size(0, view->m_viewport_size.to_type<Web::DevicePixels>());
         view->client().async_set_window_size(0, window_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 = [] {
         view->m_client_state.client->on_web_content_process_crash = [] {
             warnln("\033[31;1mWebContent Crashed!!\033[0m");
             warnln("\033[31;1mWebContent Crashed!!\033[0m");
@@ -189,7 +182,7 @@ private:
     RefPtr<ImageDecoderClient::Client> m_image_decoder_client;
     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.
     // FIXME: Allow passing the output path as an argument.
     static constexpr auto output_file_path = "output.png"sv;
     static constexpr auto output_file_path = "output.png"sv;
@@ -248,7 +241,7 @@ static StringView test_result_to_string(TestResult result)
     VERIFY_NOT_REACHED();
     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;
     Core::EventLoop loop;
     bool did_timeout = false;
     bool did_timeout = false;
@@ -258,8 +251,6 @@ static ErrorOr<TestResult> run_dump_test(HeadlessWebContentView& view, StringVie
         loop.quit(0);
         loop.quit(0);
     });
     });
 
 
-    auto url = URL::create_with_file_scheme(TRY(FileSystem::real_path(input_path)));
-
     String result;
     String result;
     auto did_finish_test = false;
     auto did_finish_test = false;
     auto did_finish_loading = 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;
     auto const color_output = isatty(STDOUT_FILENO) ? Diff::ColorOutput::Yes : Diff::ColorOutput::No;
 
 
     if (color_output == Diff::ColorOutput::Yes)
     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
     else
-        outln("\nTest failed: {}", input_path);
+        outln("\nTest failed: {}", url);
 
 
     auto hunks = TRY(Diff::from_text(expectation, actual, 3));
     auto hunks = TRY(Diff::from_text(expectation, actual, 3));
     auto out = TRY(Core::File::standard_output());
     auto out = TRY(Core::File::standard_output());
@@ -349,7 +340,7 @@ static ErrorOr<TestResult> run_dump_test(HeadlessWebContentView& view, StringVie
     return TestResult::Fail;
     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;
     Core::EventLoop loop;
     bool did_timeout = false;
     bool did_timeout = false;
@@ -370,10 +361,10 @@ static ErrorOr<TestResult> run_ref_test(HeadlessWebContentView& view, StringView
         }
         }
     };
     };
     view.on_text_test_finish = [&] {
     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();
     timeout_timer->start();
     loop.exec();
     loop.exec();
@@ -388,8 +379,8 @@ static ErrorOr<TestResult> run_ref_test(HeadlessWebContentView& view, StringView
         return TestResult::Pass;
         return TestResult::Pass;
 
 
     if (dump_failed_ref_tests) {
     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 dump_screenshot = [&](Gfx::Bitmap& bitmap, StringView path) -> ErrorOr<void> {
             auto screenshot_file = TRY(Core::File::open(path, Core::File::OpenMode::Write));
             auto screenshot_file = TRY(Core::File::open(path, Core::File::OpenMode::Write));
             auto encoded_data = TRY(Gfx::PNGWriter::encode(bitmap));
             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));
     view.load(URL::URL("about:blank"sv));
     MUST(promise->await());
     MUST(promise->await());
 
 
+    auto url = URL::create_with_file_scheme(TRY(FileSystem::real_path(input_path)));
     s_current_test_path = input_path;
     s_current_test_path = input_path;
+
     switch (mode) {
     switch (mode) {
     case TestMode::Text:
     case TestMode::Text:
     case TestMode::Layout:
     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:
     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:
     default:
         VERIFY_NOT_REACHED();
         VERIFY_NOT_REACHED();
     }
     }
@@ -633,83 +626,88 @@ static ErrorOr<int> run_tests(HeadlessWebContentView& view, StringView test_root
     return 1;
     return 1;
 }
 }
 
 
-ErrorOr<int> serenity_main(Main::Arguments arguments)
-{
-    WebView::Application app(arguments.argc, arguments.argv);
-
-    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;
+struct Application : public WebView::Application {
+    WEB_VIEW_APPLICATION(Application)
+
+    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;
     StringView test_root_path;
     ByteString test_glob;
     ByteString test_glob;
-    Vector<ByteString> certificates;
+};
+
+Application::Application(Badge<WebView::Application>, Main::Arguments&)
+{
+}
 
 
+ErrorOr<int> serenity_main(Main::Arguments arguments)
+{
     platform_init();
     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);
-
-    Core::ResourceImplementation::install(make<Core::ResourceImplementationFile>(MUST(String::from_utf8(resources_folder))));
-
-    auto theme_path = LexicalPath::join(resources_folder, "themes"sv, "Default.ini"sv);
+
+    auto app = Application::create(arguments, "about:newtab"sv);
+
+    Core::ResourceImplementation::install(make<Core::ResourceImplementationFile>(MUST(String::from_byte_string(app->resources_folder))));
+
+    auto theme_path = LexicalPath::join(app->resources_folder, "themes"sv, "Default.ini"sv);
     auto theme = TRY(Gfx::load_system_theme(theme_path.string()));
     auto theme = TRY(Gfx::load_system_theme(theme_path.string()));
 
 
     // FIXME: Allow passing the window size as an argument.
     // FIXME: Allow passing the window size as an argument.
     static constexpr Gfx::IntSize window_size { 800, 600 };
     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;
-    }
-
-    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));
+    auto view = TRY(HeadlessWebContentView::create(move(theme), window_size, app->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);
+    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);
     }
     }
 
 
-    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");
         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;
         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;
         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;
     return 0;