2022-07-06 01:18:21 +03:00
|
|
|
/*
|
2022-07-19 13:23:11 +03:00
|
|
|
* Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
|
2022-07-06 01:18:21 +03:00
|
|
|
* Copyright (c) 2022, Matthew Costa <ucosty@gmail.com>
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "Tab.h"
|
|
|
|
#include "BrowserWindow.h"
|
2022-07-14 06:41:13 +03:00
|
|
|
#include "Settings.h"
|
2022-10-04 01:59:08 +03:00
|
|
|
#include "Utilities.h"
|
2022-10-23 00:42:00 +03:00
|
|
|
#include <Browser/History.h>
|
2022-07-06 01:18:21 +03:00
|
|
|
#include <QCoreApplication>
|
2022-09-09 15:23:36 +03:00
|
|
|
#include <QFont>
|
|
|
|
#include <QFontMetrics>
|
2023-05-05 16:38:46 +03:00
|
|
|
#include <QPainter>
|
2022-10-05 16:23:41 +03:00
|
|
|
#include <QPlainTextEdit>
|
2022-07-13 05:39:38 +03:00
|
|
|
#include <QPoint>
|
2022-09-09 15:23:36 +03:00
|
|
|
#include <QResizeEvent>
|
2023-05-05 16:38:46 +03:00
|
|
|
#include <QSvgRenderer>
|
2022-07-06 01:18:21 +03:00
|
|
|
|
2022-12-04 21:43:54 +03:00
|
|
|
extern DeprecatedString s_serenity_resource_root;
|
2022-07-14 06:41:13 +03:00
|
|
|
extern Browser::Settings* s_settings;
|
2022-07-06 01:18:21 +03:00
|
|
|
|
2023-05-05 16:38:46 +03:00
|
|
|
static QIcon render_svg_icon_with_theme_colors(QString name, QPalette const& palette)
|
|
|
|
{
|
|
|
|
auto path = QString(":/Icons/%1.svg").arg(name);
|
|
|
|
|
|
|
|
QSize icon_size(16, 16);
|
|
|
|
|
|
|
|
QIcon icon;
|
|
|
|
|
|
|
|
auto render = [&](QColor color) -> QPixmap {
|
|
|
|
QImage image(icon_size, QImage::Format_ARGB32);
|
|
|
|
image.fill(Qt::transparent);
|
|
|
|
|
|
|
|
QPainter painter(&image);
|
|
|
|
QSvgRenderer renderer(path);
|
|
|
|
renderer.render(&painter);
|
|
|
|
painter.setBrush(color);
|
|
|
|
painter.setCompositionMode(QPainter::CompositionMode_SourceAtop);
|
|
|
|
painter.fillRect(image.rect(), color);
|
|
|
|
return QPixmap::fromImage(image);
|
|
|
|
};
|
|
|
|
|
|
|
|
icon.addPixmap(render(palette.color(QPalette::ColorGroup::Normal, QPalette::ColorRole::ButtonText)), QIcon::Mode::Normal);
|
|
|
|
icon.addPixmap(render(palette.color(QPalette::ColorGroup::Disabled, QPalette::ColorRole::ButtonText)), QIcon::Mode::Disabled);
|
|
|
|
|
|
|
|
return icon;
|
|
|
|
}
|
|
|
|
|
2023-04-15 03:04:28 +03:00
|
|
|
Tab::Tab(BrowserWindow* window, StringView webdriver_content_ipc_path, WebView::EnableCallgrindProfiling enable_callgrind_profiling)
|
2022-10-01 21:46:24 +03:00
|
|
|
: QWidget(window)
|
|
|
|
, m_window(window)
|
2022-07-06 01:18:21 +03:00
|
|
|
{
|
|
|
|
m_layout = new QBoxLayout(QBoxLayout::Direction::TopToBottom, this);
|
2022-09-09 15:27:59 +03:00
|
|
|
m_layout->setSpacing(0);
|
2022-07-06 01:18:21 +03:00
|
|
|
m_layout->setContentsMargins(0, 0, 0, 0);
|
|
|
|
|
2023-04-15 03:04:28 +03:00
|
|
|
m_view = new WebContentView(webdriver_content_ipc_path, enable_callgrind_profiling);
|
2022-10-01 21:46:24 +03:00
|
|
|
m_toolbar = new QToolBar(this);
|
2023-01-18 17:12:13 +03:00
|
|
|
m_location_edit = new LocationEdit(this);
|
2023-03-29 02:25:42 +03:00
|
|
|
m_reset_zoom_button = new QToolButton(m_toolbar);
|
2022-07-06 01:18:21 +03:00
|
|
|
|
2022-09-09 15:23:36 +03:00
|
|
|
m_hover_label = new QLabel(this);
|
2022-09-09 16:19:18 +03:00
|
|
|
m_hover_label->hide();
|
2022-09-09 15:23:36 +03:00
|
|
|
m_hover_label->setFrameShape(QFrame::Shape::Box);
|
|
|
|
m_hover_label->setAutoFillBackground(true);
|
|
|
|
|
2022-09-12 10:12:04 +03:00
|
|
|
auto* focus_location_editor_action = new QAction("Edit Location");
|
|
|
|
focus_location_editor_action->setShortcut(QKeySequence("Ctrl+L"));
|
|
|
|
addAction(focus_location_editor_action);
|
2022-07-07 03:56:09 +03:00
|
|
|
|
2022-07-06 01:18:21 +03:00
|
|
|
m_layout->addWidget(m_toolbar);
|
|
|
|
m_layout->addWidget(m_view);
|
|
|
|
|
2023-05-05 16:38:46 +03:00
|
|
|
rerender_toolbar_icons();
|
|
|
|
|
2023-05-13 15:07:12 +03:00
|
|
|
m_toolbar->addAction(&m_window->go_back_action());
|
|
|
|
m_toolbar->addAction(&m_window->go_forward_action());
|
|
|
|
m_toolbar->addAction(&m_window->reload_action());
|
2022-07-06 01:18:21 +03:00
|
|
|
m_toolbar->addWidget(m_location_edit);
|
2023-03-31 23:33:42 +03:00
|
|
|
m_reset_zoom_button->setToolTip("Reset zoom level");
|
2023-03-29 02:25:42 +03:00
|
|
|
m_reset_zoom_button_action = m_toolbar->addWidget(m_reset_zoom_button);
|
|
|
|
m_reset_zoom_button_action->setVisible(false);
|
|
|
|
|
|
|
|
QObject::connect(m_reset_zoom_button, &QAbstractButton::clicked, [this] {
|
|
|
|
view().reset_zoom();
|
|
|
|
update_reset_zoom_button();
|
|
|
|
});
|
|
|
|
|
|
|
|
QObject::connect(m_view, &WebContentView::link_unhovered, [this] {
|
|
|
|
m_hover_label->hide();
|
|
|
|
});
|
2022-07-06 01:18:21 +03:00
|
|
|
|
2023-03-21 02:52:00 +03:00
|
|
|
QObject::connect(m_view, &WebContentView::activate_tab, [this] {
|
|
|
|
m_window->activate_tab(tab_index());
|
|
|
|
});
|
|
|
|
|
2023-03-07 06:13:08 +03:00
|
|
|
QObject::connect(m_view, &WebContentView::close, [this] {
|
|
|
|
m_window->close_tab(tab_index());
|
|
|
|
});
|
|
|
|
|
2022-10-05 16:23:41 +03:00
|
|
|
QObject::connect(m_view, &WebContentView::link_hovered, [this](QString const& title) {
|
2022-09-09 15:23:36 +03:00
|
|
|
m_hover_label->setText(title);
|
|
|
|
update_hover_label();
|
|
|
|
m_hover_label->show();
|
2022-07-13 05:39:38 +03:00
|
|
|
});
|
2022-10-05 16:23:41 +03:00
|
|
|
QObject::connect(m_view, &WebContentView::link_unhovered, [this] {
|
2022-09-09 15:23:36 +03:00
|
|
|
m_hover_label->hide();
|
2022-07-13 05:39:38 +03:00
|
|
|
});
|
2022-07-06 01:18:21 +03:00
|
|
|
|
2022-11-08 04:00:24 +03:00
|
|
|
QObject::connect(m_view, &WebContentView::back_mouse_button, [this] {
|
|
|
|
back();
|
|
|
|
});
|
|
|
|
|
|
|
|
QObject::connect(m_view, &WebContentView::forward_mouse_button, [this] {
|
|
|
|
forward();
|
|
|
|
});
|
|
|
|
|
2022-11-24 03:52:16 +03:00
|
|
|
QObject::connect(m_view, &WebContentView::load_started, [this](const URL& url, bool is_redirect) {
|
|
|
|
// If we are loading due to a redirect, we replace the current history entry
|
|
|
|
// with the loaded URL
|
|
|
|
if (is_redirect) {
|
|
|
|
m_history.replace_current(url, m_title.toUtf8().data());
|
|
|
|
}
|
|
|
|
|
2022-12-04 21:43:54 +03:00
|
|
|
m_location_edit->setText(url.to_deprecated_string().characters());
|
2023-02-01 21:51:56 +03:00
|
|
|
m_location_edit->setCursorPosition(0);
|
2022-11-08 15:01:20 +03:00
|
|
|
|
|
|
|
// Don't add to history if back or forward is pressed
|
|
|
|
if (!m_is_history_navigation) {
|
|
|
|
m_history.push(url, m_title.toUtf8().data());
|
|
|
|
}
|
|
|
|
m_is_history_navigation = false;
|
|
|
|
|
2023-05-13 15:07:12 +03:00
|
|
|
m_window->go_back_action().setEnabled(m_history.can_go_back());
|
|
|
|
m_window->go_forward_action().setEnabled(m_history.can_go_forward());
|
2022-07-06 16:38:50 +03:00
|
|
|
});
|
2022-07-06 01:18:21 +03:00
|
|
|
QObject::connect(m_location_edit, &QLineEdit::returnPressed, this, &Tab::location_edit_return_pressed);
|
2022-10-05 16:23:41 +03:00
|
|
|
QObject::connect(m_view, &WebContentView::title_changed, this, &Tab::page_title_changed);
|
|
|
|
QObject::connect(m_view, &WebContentView::favicon_changed, this, &Tab::page_favicon_changed);
|
2022-09-12 10:12:04 +03:00
|
|
|
QObject::connect(focus_location_editor_action, &QAction::triggered, this, &Tab::focus_location_editor);
|
2022-10-05 16:23:41 +03:00
|
|
|
|
|
|
|
QObject::connect(m_view, &WebContentView::got_source, this, [this](AK::URL, QString const& source) {
|
|
|
|
auto* text_edit = new QPlainTextEdit(this);
|
|
|
|
text_edit->setWindowFlags(Qt::Window);
|
|
|
|
text_edit->setFont(QFontDatabase::systemFont(QFontDatabase::SystemFont::FixedFont));
|
|
|
|
text_edit->resize(800, 600);
|
|
|
|
text_edit->setPlainText(source);
|
|
|
|
text_edit->show();
|
|
|
|
});
|
2022-10-07 14:35:40 +03:00
|
|
|
|
2022-11-14 19:24:06 +03:00
|
|
|
QObject::connect(m_view, &WebContentView::navigate_back, this, &Tab::back);
|
|
|
|
QObject::connect(m_view, &WebContentView::navigate_forward, this, &Tab::forward);
|
|
|
|
QObject::connect(m_view, &WebContentView::refresh, this, &Tab::reload);
|
|
|
|
QObject::connect(m_view, &WebContentView::restore_window, this, [this]() {
|
|
|
|
m_window->showNormal();
|
|
|
|
});
|
|
|
|
QObject::connect(m_view, &WebContentView::reposition_window, this, [this](auto const& position) {
|
|
|
|
m_window->move(position.x(), position.y());
|
|
|
|
return Gfx::IntPoint { m_window->x(), m_window->y() };
|
|
|
|
});
|
|
|
|
QObject::connect(m_view, &WebContentView::resize_window, this, [this](auto const& size) {
|
|
|
|
m_window->resize(size.width(), size.height());
|
|
|
|
return Gfx::IntSize { m_window->width(), m_window->height() };
|
|
|
|
});
|
|
|
|
QObject::connect(m_view, &WebContentView::maximize_window, this, [this]() {
|
|
|
|
m_window->showMaximized();
|
|
|
|
return Gfx::IntRect { m_window->x(), m_window->y(), m_window->width(), m_window->height() };
|
|
|
|
});
|
|
|
|
QObject::connect(m_view, &WebContentView::minimize_window, this, [this]() {
|
|
|
|
m_window->showMinimized();
|
|
|
|
return Gfx::IntRect { m_window->x(), m_window->y(), m_window->width(), m_window->height() };
|
|
|
|
});
|
|
|
|
QObject::connect(m_view, &WebContentView::fullscreen_window, this, [this]() {
|
|
|
|
m_window->showFullScreen();
|
|
|
|
return Gfx::IntRect { m_window->x(), m_window->y(), m_window->width(), m_window->height() };
|
|
|
|
});
|
2022-09-12 10:12:04 +03:00
|
|
|
}
|
|
|
|
|
2023-03-29 02:25:42 +03:00
|
|
|
void Tab::update_reset_zoom_button()
|
|
|
|
{
|
|
|
|
auto zoom_level = view().zoom_level();
|
|
|
|
if (zoom_level != 1.0f) {
|
|
|
|
auto zoom_level_text = MUST(String::formatted("{}%", round_to<int>(zoom_level * 100)));
|
|
|
|
m_reset_zoom_button->setText(qstring_from_ak_string(zoom_level_text));
|
|
|
|
m_reset_zoom_button_action->setVisible(true);
|
|
|
|
} else {
|
|
|
|
m_reset_zoom_button_action->setVisible(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-12 10:12:04 +03:00
|
|
|
void Tab::focus_location_editor()
|
|
|
|
{
|
|
|
|
m_location_edit->setFocus();
|
|
|
|
m_location_edit->selectAll();
|
2022-07-06 16:38:50 +03:00
|
|
|
}
|
|
|
|
|
2023-01-08 17:01:26 +03:00
|
|
|
void Tab::navigate(QString url, LoadType load_type)
|
2022-07-06 16:38:50 +03:00
|
|
|
{
|
2023-01-19 21:39:25 +03:00
|
|
|
if (!url.startsWith("http://", Qt::CaseInsensitive) && !url.startsWith("https://", Qt::CaseInsensitive) && !url.startsWith("file://", Qt::CaseInsensitive) && !url.startsWith("about:", Qt::CaseInsensitive))
|
2022-07-14 18:32:51 +03:00
|
|
|
url = "http://" + url;
|
2023-01-08 17:01:26 +03:00
|
|
|
m_is_history_navigation = (load_type == LoadType::HistoryNavigation);
|
2022-12-04 21:43:54 +03:00
|
|
|
view().load(ak_deprecated_string_from_qstring(url));
|
2022-07-06 16:38:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void Tab::back()
|
|
|
|
{
|
|
|
|
if (!m_history.can_go_back())
|
|
|
|
return;
|
|
|
|
|
2022-11-08 15:01:20 +03:00
|
|
|
m_is_history_navigation = true;
|
2022-07-06 16:38:50 +03:00
|
|
|
m_history.go_back();
|
2022-12-04 21:43:54 +03:00
|
|
|
view().load(m_history.current().url.to_deprecated_string());
|
2022-07-06 16:38:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void Tab::forward()
|
|
|
|
{
|
|
|
|
if (!m_history.can_go_forward())
|
|
|
|
return;
|
|
|
|
|
2022-11-08 15:01:20 +03:00
|
|
|
m_is_history_navigation = true;
|
2022-07-06 16:38:50 +03:00
|
|
|
m_history.go_forward();
|
2022-12-04 21:43:54 +03:00
|
|
|
view().load(m_history.current().url.to_deprecated_string());
|
2022-07-06 16:38:50 +03:00
|
|
|
}
|
|
|
|
|
2022-07-06 01:18:21 +03:00
|
|
|
void Tab::reload()
|
|
|
|
{
|
2022-11-09 02:35:55 +03:00
|
|
|
m_is_history_navigation = true;
|
2022-12-04 21:43:54 +03:00
|
|
|
view().load(m_history.current().url.to_deprecated_string());
|
2022-07-06 01:18:21 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void Tab::location_edit_return_pressed()
|
|
|
|
{
|
2022-07-06 16:38:50 +03:00
|
|
|
navigate(m_location_edit->text());
|
2022-07-06 01:18:21 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void Tab::page_title_changed(QString title)
|
|
|
|
{
|
2022-07-06 16:38:50 +03:00
|
|
|
m_title = title;
|
2022-12-04 21:43:54 +03:00
|
|
|
m_history.update_title(ak_deprecated_string_from_qstring(m_title));
|
2022-07-06 01:18:21 +03:00
|
|
|
emit title_changed(tab_index(), std::move(title));
|
|
|
|
}
|
|
|
|
|
|
|
|
void Tab::page_favicon_changed(QIcon icon)
|
|
|
|
{
|
|
|
|
emit favicon_changed(tab_index(), std::move(icon));
|
|
|
|
}
|
|
|
|
|
|
|
|
int Tab::tab_index()
|
|
|
|
{
|
2022-09-21 22:17:13 +03:00
|
|
|
return m_window->tab_index(this);
|
2022-07-06 01:18:21 +03:00
|
|
|
}
|
2022-07-08 15:14:40 +03:00
|
|
|
|
2022-12-04 21:43:54 +03:00
|
|
|
void Tab::debug_request(DeprecatedString const& request, DeprecatedString const& argument)
|
2022-07-08 15:14:40 +03:00
|
|
|
{
|
2022-10-04 00:53:04 +03:00
|
|
|
if (request == "dump-history")
|
|
|
|
m_history.dump();
|
|
|
|
else
|
|
|
|
m_view->debug_request(request, argument);
|
2022-07-08 15:14:40 +03:00
|
|
|
}
|
2022-09-09 15:23:36 +03:00
|
|
|
|
|
|
|
void Tab::resizeEvent(QResizeEvent* event)
|
|
|
|
{
|
|
|
|
QWidget::resizeEvent(event);
|
|
|
|
if (m_hover_label->isVisible())
|
|
|
|
update_hover_label();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Tab::update_hover_label()
|
|
|
|
{
|
|
|
|
m_hover_label->resize(QFontMetrics(m_hover_label->font()).boundingRect(m_hover_label->text()).adjusted(-4, -2, 4, 2).size());
|
|
|
|
m_hover_label->move(6, height() - m_hover_label->height() - 8);
|
|
|
|
m_hover_label->raise();
|
|
|
|
}
|
2023-05-05 16:38:46 +03:00
|
|
|
|
|
|
|
bool Tab::event(QEvent* event)
|
|
|
|
{
|
|
|
|
if (event->type() == QEvent::PaletteChange) {
|
|
|
|
rerender_toolbar_icons();
|
|
|
|
return QWidget::event(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
return QWidget::event(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Tab::rerender_toolbar_icons()
|
|
|
|
{
|
2023-05-13 15:07:12 +03:00
|
|
|
m_window->go_back_action().setIcon(render_svg_icon_with_theme_colors("back", palette()));
|
|
|
|
m_window->go_forward_action().setIcon(render_svg_icon_with_theme_colors("forward", palette()));
|
|
|
|
m_window->reload_action().setIcon(render_svg_icon_with_theme_colors("reload", palette()));
|
2023-05-05 16:38:46 +03:00
|
|
|
}
|