Skip to content

Commit 5328b14

Browse files
committed
Updating to upstream
1 parent fb1388c commit 5328b14

19 files changed

+1401
-363
lines changed

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
.qmake.stash
22
Makefile
3-
ladybird
3+
coccinellidae
44
*.o
55
moc_*

BrowserWindow.cpp

+218-20
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,233 @@
1+
/*
2+
* Copyright (c) 2022, Andreas Kling <[email protected]>
3+
* Copyright (c) 2022, Matthew Costa <[email protected]>
4+
*
5+
* SPDX-License-Identifier: BSD-2-Clause
6+
*/
7+
18
#include "BrowserWindow.h"
29
#include "WebView.h"
3-
#include <QStatusBar>
10+
#include <LibCore/EventLoop.h>
11+
#include <QAction>
12+
#include <QPlainTextEdit>
13+
14+
extern String s_serenity_resource_root;
415

5-
BrowserWindow::BrowserWindow()
16+
BrowserWindow::BrowserWindow(Core::EventLoop& event_loop)
17+
: m_event_loop(event_loop)
618
{
7-
m_toolbar = new QToolBar;
8-
m_location_edit = new QLineEdit;
9-
m_toolbar->addWidget(m_location_edit);
19+
m_tabs_container = new QTabWidget;
20+
m_tabs_container->setElideMode(Qt::TextElideMode::ElideRight);
21+
m_tabs_container->setMovable(true);
22+
m_tabs_container->setTabsClosable(true);
23+
24+
m_tabs_bar = m_tabs_container->findChild<QTabBar*>();
25+
m_tabs_bar->hide();
26+
27+
auto* menu = menuBar()->addMenu("&File");
28+
29+
auto* new_tab_action = new QAction("New &Tab");
30+
new_tab_action->setShortcut(QKeySequence("Ctrl+T"));
31+
menu->addAction(new_tab_action);
32+
33+
auto* quit_action = new QAction("&Quit");
34+
quit_action->setShortcut(QKeySequence("Ctrl+Q"));
35+
menu->addAction(quit_action);
36+
37+
auto* inspect_menu = menuBar()->addMenu("&Inspect");
38+
39+
auto* view_source_action = new QAction("View &Source");
40+
view_source_action->setIcon(QIcon(QString("%1/res/icons/16x16/filetype-html.png").arg(s_serenity_resource_root.characters())));
41+
view_source_action->setShortcut(QKeySequence("CTRL + U"));
42+
inspect_menu->addAction(view_source_action);
43+
QObject::connect(view_source_action, &QAction::triggered, this, [this] {
44+
if (m_current_tab) {
45+
auto source = m_current_tab->view().source();
46+
47+
auto* text_edit = new QPlainTextEdit;
48+
text_edit->setFont(QFontDatabase::systemFont(QFontDatabase::SystemFont::FixedFont));
49+
text_edit->resize(800, 600);
50+
text_edit->setPlainText(source.characters());
51+
text_edit->show();
52+
}
53+
});
54+
55+
auto* debug_menu = menuBar()->addMenu("&Debug");
56+
57+
auto* dump_dom_tree_action = new QAction("Dump DOM Tree");
58+
dump_dom_tree_action->setIcon(QIcon(QString("%1/res/icons/browser/dom-tree.png").arg(s_serenity_resource_root.characters())));
59+
debug_menu->addAction(dump_dom_tree_action);
60+
QObject::connect(dump_dom_tree_action, &QAction::triggered, this, [this] {
61+
debug_request("dump-dom-tree");
62+
});
63+
64+
auto* dump_layout_tree_action = new QAction("Dump Layout Tree");
65+
dump_layout_tree_action->setIcon(QIcon(QString("%1/res/icons/16x16/layout.png").arg(s_serenity_resource_root.characters())));
66+
debug_menu->addAction(dump_layout_tree_action);
67+
QObject::connect(dump_layout_tree_action, &QAction::triggered, this, [this] {
68+
debug_request("dump-layout-tree");
69+
});
70+
71+
auto* dump_stacking_context_tree_action = new QAction("Dump Stacking Context Tree");
72+
dump_stacking_context_tree_action->setIcon(QIcon(QString("%1/res/icons/16x16/layers.png").arg(s_serenity_resource_root.characters())));
73+
debug_menu->addAction(dump_stacking_context_tree_action);
74+
QObject::connect(dump_stacking_context_tree_action, &QAction::triggered, this, [this] {
75+
debug_request("dump-stacking-context-tree");
76+
});
77+
78+
auto* dump_style_sheets_action = new QAction("Dump Style Sheets");
79+
dump_style_sheets_action->setIcon(QIcon(QString("%1/res/icons/16x16/filetype-css.png").arg(s_serenity_resource_root.characters())));
80+
debug_menu->addAction(dump_style_sheets_action);
81+
QObject::connect(dump_style_sheets_action, &QAction::triggered, this, [this] {
82+
debug_request("dump-style-sheets");
83+
});
84+
85+
auto* dump_history_action = new QAction("Dump History");
86+
dump_history_action->setIcon(QIcon(QString("%1/res/icons/16x16/history.png").arg(s_serenity_resource_root.characters())));
87+
debug_menu->addAction(dump_history_action);
88+
QObject::connect(dump_history_action, &QAction::triggered, this, [this] {
89+
debug_request("dump-history");
90+
});
91+
92+
auto* dump_cookies_action = new QAction("Dump Cookies");
93+
dump_cookies_action->setIcon(QIcon(QString("%1/res/icons/browser/cookie.png").arg(s_serenity_resource_root.characters())));
94+
debug_menu->addAction(dump_cookies_action);
95+
QObject::connect(dump_cookies_action, &QAction::triggered, this, [this] {
96+
debug_request("dump-cookies");
97+
});
98+
99+
auto* dump_local_storage_action = new QAction("Dump Local Storage");
100+
dump_local_storage_action->setIcon(QIcon(QString("%1/res/icons/browser/local-storage.png").arg(s_serenity_resource_root.characters())));
101+
debug_menu->addAction(dump_local_storage_action);
102+
QObject::connect(dump_local_storage_action, &QAction::triggered, this, [this] {
103+
debug_request("dump-local-storage");
104+
});
105+
106+
debug_menu->addSeparator();
107+
108+
auto* show_line_box_borders_action = new QAction("Show Line Box Borders");
109+
show_line_box_borders_action->setCheckable(true);
110+
debug_menu->addAction(show_line_box_borders_action);
111+
QObject::connect(show_line_box_borders_action, &QAction::triggered, this, [this, show_line_box_borders_action] {
112+
bool state = show_line_box_borders_action->isChecked();
113+
debug_request("set-line-box-borders", state ? "on" : "off");
114+
});
115+
116+
debug_menu->addSeparator();
117+
118+
auto* collect_garbage_action = new QAction("Collect Garbage!!");
119+
collect_garbage_action->setIcon(QIcon(QString("%1/res/icons/16x16/trash-can.png").arg(s_serenity_resource_root.characters())));
120+
debug_menu->addAction(collect_garbage_action);
121+
QObject::connect(collect_garbage_action, &QAction::triggered, this, [this] {
122+
debug_request("collect-garbage");
123+
});
124+
125+
auto* clear_cache_action = new QAction("Clear Cache");
126+
clear_cache_action->setIcon(QIcon(QString("%1/res/icons/browser/clear-cache.png").arg(s_serenity_resource_root.characters())));
127+
debug_menu->addAction(clear_cache_action);
128+
QObject::connect(clear_cache_action, &QAction::triggered, this, [this] {
129+
debug_request("clear-cache");
130+
});
131+
132+
debug_menu->addSeparator();
10133

11-
addToolBar(m_toolbar);
134+
auto* enable_scripting_action = new QAction("Enable Scripting");
135+
enable_scripting_action->setCheckable(true);
136+
enable_scripting_action->setChecked(true);
137+
debug_menu->addAction(enable_scripting_action);
138+
QObject::connect(enable_scripting_action, &QAction::triggered, this, [this, enable_scripting_action] {
139+
bool state = enable_scripting_action->isChecked();
140+
debug_request("scripting", state ? "on" : "off");
141+
});
12142

13-
m_view = new WebView;
14-
setCentralWidget(m_view);
143+
auto* enable_same_origin_policy_action = new QAction("Enable Same-Origin Policy");
144+
enable_same_origin_policy_action->setCheckable(true);
145+
debug_menu->addAction(enable_same_origin_policy_action);
146+
QObject::connect(enable_same_origin_policy_action, &QAction::triggered, this, [this, enable_same_origin_policy_action] {
147+
bool state = enable_same_origin_policy_action->isChecked();
148+
debug_request("same-origin-policy", state ? "on" : "off");
149+
});
15150

16-
QObject::connect(m_view, &WebView::linkHovered, statusBar(), &QStatusBar::showMessage);
17-
QObject::connect(m_view, &WebView::linkUnhovered, statusBar(), &QStatusBar::clearMessage);
151+
QObject::connect(new_tab_action, &QAction::triggered, this, &BrowserWindow::new_tab);
152+
QObject::connect(quit_action, &QAction::triggered, this, &QMainWindow::close);
153+
QObject::connect(m_tabs_container, &QTabWidget::currentChanged, [this](int index) {
154+
setWindowTitle(QString("%1 - Coccinellidae").arg(m_tabs_container->tabText(index)));
155+
setWindowIcon(m_tabs_container->tabIcon(index));
156+
});
157+
QObject::connect(m_tabs_container, &QTabWidget::tabCloseRequested, this, &BrowserWindow::close_tab);
18158

19-
QObject::connect(m_view, &WebView::loadStarted, m_location_edit, &QLineEdit::setText);
20-
QObject::connect(m_location_edit, &QLineEdit::returnPressed, this, &BrowserWindow::location_edit_return_pressed);
21-
QObject::connect(m_view, &WebView::title_changed, this, &BrowserWindow::page_title_changed);
159+
new_tab();
160+
161+
setCentralWidget(m_tabs_container);
162+
}
163+
164+
void BrowserWindow::debug_request(String const& request, String const& argument)
165+
{
166+
if (!m_current_tab)
167+
return;
168+
m_current_tab->debug_request(request, argument);
169+
}
170+
171+
void BrowserWindow::new_tab()
172+
{
173+
auto tab = make<Tab>(this);
174+
auto tab_ptr = tab.ptr();
175+
m_tabs.append(std::move(tab));
176+
177+
if (m_current_tab == nullptr) {
178+
m_current_tab = tab_ptr;
179+
}
180+
181+
m_tabs_container->addTab(tab_ptr, "New Tab");
182+
m_tabs_container->setCurrentWidget(tab_ptr);
183+
184+
QObject::connect(tab_ptr, &Tab::title_changed, this, &BrowserWindow::tab_title_changed);
185+
QObject::connect(tab_ptr, &Tab::favicon_changed, this, &BrowserWindow::tab_favicon_changed);
186+
187+
if (m_tabs_container->count() > 1)
188+
m_tabs_bar->show();
22189
}
23190

24-
void BrowserWindow::location_edit_return_pressed()
191+
void BrowserWindow::close_tab(int index)
25192
{
26-
view().load(m_location_edit->text().toUtf8().data());
193+
auto* tab = m_tabs_container->widget(index);
194+
m_tabs_container->removeTab(index);
195+
m_tabs.remove_first_matching([&](auto& entry) {
196+
return entry == tab;
197+
});
198+
199+
if (m_tabs_container->count() <= 1)
200+
m_tabs_bar->hide();
201+
}
202+
203+
int BrowserWindow::tab_index(Tab* tab)
204+
{
205+
return m_tabs_container->indexOf(tab);
27206
}
28207

29-
void BrowserWindow::page_title_changed(QString title)
208+
void BrowserWindow::tab_title_changed(int index, QString const& title)
30209
{
31-
if (title.isEmpty())
32-
setWindowTitle("Ladybird");
33-
else
34-
setWindowTitle(QString("%1 - Ladybird").arg(title));
210+
if (title.isEmpty()) {
211+
m_tabs_container->setTabText(index, "...");
212+
setWindowTitle("Coccinellidae");
213+
} else {
214+
m_tabs_container->setTabText(index, title);
215+
setWindowTitle(QString("%1 - Coccinellidae").arg(title));
216+
}
217+
}
218+
219+
void BrowserWindow::tab_favicon_changed(int index, QIcon icon)
220+
{
221+
m_tabs_container->setTabIcon(index, icon);
222+
setWindowIcon(icon);
223+
}
224+
225+
void BrowserWindow::closeEvent(QCloseEvent* event)
226+
{
227+
QWidget::closeEvent(event);
228+
229+
// FIXME: Coccinellidae only supports one window at the moment. When we support
230+
// multiple windows, we'll only want to fire off the quit event when
231+
// all of the browser windows have closed.
232+
m_event_loop.quit(0);
35233
}

BrowserWindow.h

+31-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
1+
/*
2+
* Copyright (c) 2022, Andreas Kling <[email protected]>
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
7+
#include "Tab.h"
8+
#include <AK/NonnullOwnPtrVector.h>
9+
#include <LibCore/Forward.h>
10+
#include <QIcon>
111
#include <QLineEdit>
212
#include <QMainWindow>
13+
#include <QMenuBar>
14+
#include <QTabBar>
15+
#include <QTabWidget>
316
#include <QToolBar>
417

518
#pragma once
@@ -9,16 +22,27 @@ class WebView;
922
class BrowserWindow : public QMainWindow {
1023
Q_OBJECT
1124
public:
12-
BrowserWindow();
25+
explicit BrowserWindow(Core::EventLoop&);
26+
27+
WebView& view() const { return m_current_tab->view(); }
28+
29+
int tab_index(Tab*);
1330

14-
WebView& view() { return *m_view; }
31+
virtual void closeEvent(QCloseEvent*) override;
1532

1633
public slots:
17-
void location_edit_return_pressed();
18-
void page_title_changed(QString);
34+
void tab_title_changed(int index, QString const&);
35+
void tab_favicon_changed(int index, QIcon icon);
36+
void new_tab();
37+
void close_tab(int index);
1938

2039
private:
21-
QToolBar* m_toolbar { nullptr };
22-
QLineEdit* m_location_edit { nullptr };
23-
WebView* m_view { nullptr };
40+
void debug_request(String const& request, String const& argument = "");
41+
42+
QTabWidget* m_tabs_container { nullptr };
43+
QTabBar* m_tabs_bar { nullptr };
44+
NonnullOwnPtrVector<Tab> m_tabs;
45+
Tab* m_current_tab { nullptr };
46+
47+
Core::EventLoop& m_event_loop;
2448
};

CMakeLists.txt

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
cmake_minimum_required(VERSION 3.16...3.22)
2+
3+
project(coccinellidae
4+
VERSION 0.0.1
5+
LANGUAGES CXX
6+
DESCRIPTION "Coccinellidae Web Browser"
7+
)
8+
9+
set(CMAKE_CXX_STANDARD 20)
10+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
11+
set(CMAKE_CXX_EXTENSIONS OFF)
12+
13+
set(CMAKE_SKIP_BUILD_RPATH FALSE)
14+
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
15+
# See slide 100 of the following ppt :^)
16+
# https://crascit.com/wp-content/uploads/2019/09/Deep-CMake-For-Library-Authors-Craig-Scott-CppCon-2019.pdf
17+
if (NOT APPLE)
18+
set(CMAKE_INSTALL_RPATH $ORIGIN)
19+
endif()
20+
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
21+
22+
include(cmake/EnableLLD.cmake)
23+
24+
# Lagom
25+
include(FetchContent)
26+
include(cmake/FetchLagom.cmake)
27+
28+
# Lagom warnings
29+
include(${Lagom_SOURCE_DIR}/../CMake/lagom_compile_options.cmake)
30+
add_compile_options(-Wno-expansion-to-defined)
31+
add_compile_options(-Wno-user-defined-literals)
32+
33+
set(CMAKE_AUTOMOC ON)
34+
35+
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network Widgets REQUIRED)
36+
find_package(Qt5 COMPONENTS Core Gui Network Widgets )
37+
#find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network Widgets )
38+
39+
set(SOURCES
40+
BrowserWindow.cpp
41+
CookieJar.cpp
42+
RequestManagerQt.cpp
43+
main.cpp
44+
WebView.cpp
45+
History.cpp
46+
Tab.cpp
47+
)
48+
49+
add_executable(coccinellidae ${SOURCES})
50+
target_link_libraries(coccinellidae PRIVATE Qt5::Widgets Qt5::Network Lagom::Web Lagom::WebSocket Lagom::Main)
51+
#target_link_libraries(coccinellidae PRIVATE Qt::Widgets Qt::Network Lagom::Web Lagom::WebSocket Lagom::Main)
52+
53+
get_filename_component(
54+
SERENITY_SOURCE_DIR "${Lagom_SOURCE_DIR}/../.."
55+
ABSOLUTE
56+
)
57+
58+
add_custom_target(run
59+
COMMAND "${CMAKE_COMMAND}" -E env "SERENITY_SOURCE_DIR=${SERENITY_SOURCE_DIR}" "$<TARGET_FILE:coccinellidae>"
60+
USES_TERMINAL
61+
)
62+
63+
add_custom_target(debug
64+
COMMAND "${CMAKE_COMMAND}" -E env "SERENITY_SOURCE_DIR=${SERENITY_SOURCE_DIR}" gdb "$<TARGET_FILE:coccinellidae>"
65+
USES_TERMINAL
66+
)
67+

0 commit comments

Comments
 (0)