Serenity Operating System
at master 241 lines 8.9 kB view raw
1/* 2 * Copyright (c) 2022, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2022, Matthew Costa <ucosty@gmail.com> 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include "Tab.h" 9#include "BrowserWindow.h" 10#include "Settings.h" 11#include "Utilities.h" 12#include <Browser/History.h> 13#include <QCoreApplication> 14#include <QFont> 15#include <QFontMetrics> 16#include <QPlainTextEdit> 17#include <QPoint> 18#include <QResizeEvent> 19 20extern DeprecatedString s_serenity_resource_root; 21extern Browser::Settings* s_settings; 22 23Tab::Tab(BrowserWindow* window, StringView webdriver_content_ipc_path) 24 : QWidget(window) 25 , m_window(window) 26{ 27 m_layout = new QBoxLayout(QBoxLayout::Direction::TopToBottom, this); 28 m_layout->setSpacing(0); 29 m_layout->setContentsMargins(0, 0, 0, 0); 30 31 m_view = new WebContentView(webdriver_content_ipc_path); 32 m_toolbar = new QToolBar(this); 33 m_location_edit = new LocationEdit(this); 34 35 m_hover_label = new QLabel(this); 36 m_hover_label->hide(); 37 m_hover_label->setFrameShape(QFrame::Shape::Box); 38 m_hover_label->setAutoFillBackground(true); 39 40 auto* focus_location_editor_action = new QAction("Edit Location"); 41 focus_location_editor_action->setShortcut(QKeySequence("Ctrl+L")); 42 addAction(focus_location_editor_action); 43 44 m_layout->addWidget(m_toolbar); 45 m_layout->addWidget(m_view); 46 47 auto back_icon_path = QString("%1/res/icons/16x16/go-back.png").arg(s_serenity_resource_root.characters()); 48 auto forward_icon_path = QString("%1/res/icons/16x16/go-forward.png").arg(s_serenity_resource_root.characters()); 49 auto home_icon_path = QString("%1/res/icons/16x16/go-home.png").arg(s_serenity_resource_root.characters()); 50 auto reload_icon_path = QString("%1/res/icons/16x16/reload.png").arg(s_serenity_resource_root.characters()); 51 m_back_action = make<QAction>(QIcon(back_icon_path), "Back"); 52 m_back_action->setEnabled(false); 53 m_back_action->setShortcuts(QKeySequence::keyBindings(QKeySequence::StandardKey::Back)); 54 m_forward_action = make<QAction>(QIcon(forward_icon_path), "Forward"); 55 m_forward_action->setEnabled(false); 56 m_forward_action->setShortcuts(QKeySequence::keyBindings(QKeySequence::StandardKey::Forward)); 57 m_home_action = make<QAction>(QIcon(home_icon_path), "Home"); 58 m_reload_action = make<QAction>(QIcon(reload_icon_path), "Reload"); 59 m_reload_action->setShortcuts(QKeySequence::keyBindings(QKeySequence::StandardKey::Refresh)); 60 61 m_toolbar->addAction(m_back_action); 62 m_toolbar->addAction(m_forward_action); 63 m_toolbar->addAction(m_reload_action); 64 m_toolbar->addAction(m_home_action); 65 m_toolbar->addWidget(m_location_edit); 66 67 QObject::connect(m_view, &WebContentView::close, [this] { 68 m_window->close_tab(tab_index()); 69 }); 70 71 QObject::connect(m_view, &WebContentView::link_hovered, [this](QString const& title) { 72 m_hover_label->setText(title); 73 update_hover_label(); 74 m_hover_label->show(); 75 }); 76 QObject::connect(m_view, &WebContentView::link_unhovered, [this] { 77 m_hover_label->hide(); 78 }); 79 80 QObject::connect(m_view, &WebContentView::back_mouse_button, [this] { 81 back(); 82 }); 83 84 QObject::connect(m_view, &WebContentView::forward_mouse_button, [this] { 85 forward(); 86 }); 87 88 QObject::connect(m_view, &WebContentView::load_started, [this](const URL& url, bool is_redirect) { 89 // If we are loading due to a redirect, we replace the current history entry 90 // with the loaded URL 91 if (is_redirect) { 92 m_history.replace_current(url, m_title.toUtf8().data()); 93 } 94 95 m_location_edit->setText(url.to_deprecated_string().characters()); 96 m_location_edit->setCursorPosition(0); 97 98 // Don't add to history if back or forward is pressed 99 if (!m_is_history_navigation) { 100 m_history.push(url, m_title.toUtf8().data()); 101 } 102 m_is_history_navigation = false; 103 104 m_back_action->setEnabled(m_history.can_go_back()); 105 m_forward_action->setEnabled(m_history.can_go_forward()); 106 }); 107 QObject::connect(m_location_edit, &QLineEdit::returnPressed, this, &Tab::location_edit_return_pressed); 108 QObject::connect(m_view, &WebContentView::title_changed, this, &Tab::page_title_changed); 109 QObject::connect(m_view, &WebContentView::favicon_changed, this, &Tab::page_favicon_changed); 110 111 QObject::connect(m_back_action, &QAction::triggered, this, &Tab::back); 112 QObject::connect(m_forward_action, &QAction::triggered, this, &Tab::forward); 113 QObject::connect(m_home_action, &QAction::triggered, this, &Tab::home); 114 QObject::connect(m_reload_action, &QAction::triggered, this, &Tab::reload); 115 QObject::connect(focus_location_editor_action, &QAction::triggered, this, &Tab::focus_location_editor); 116 117 QObject::connect(m_view, &WebContentView::got_source, this, [this](AK::URL, QString const& source) { 118 auto* text_edit = new QPlainTextEdit(this); 119 text_edit->setWindowFlags(Qt::Window); 120 text_edit->setFont(QFontDatabase::systemFont(QFontDatabase::SystemFont::FixedFont)); 121 text_edit->resize(800, 600); 122 text_edit->setPlainText(source); 123 text_edit->show(); 124 }); 125 126 QObject::connect(m_view, &WebContentView::navigate_back, this, &Tab::back); 127 QObject::connect(m_view, &WebContentView::navigate_forward, this, &Tab::forward); 128 QObject::connect(m_view, &WebContentView::refresh, this, &Tab::reload); 129 QObject::connect(m_view, &WebContentView::restore_window, this, [this]() { 130 m_window->showNormal(); 131 }); 132 QObject::connect(m_view, &WebContentView::reposition_window, this, [this](auto const& position) { 133 m_window->move(position.x(), position.y()); 134 return Gfx::IntPoint { m_window->x(), m_window->y() }; 135 }); 136 QObject::connect(m_view, &WebContentView::resize_window, this, [this](auto const& size) { 137 m_window->resize(size.width(), size.height()); 138 return Gfx::IntSize { m_window->width(), m_window->height() }; 139 }); 140 QObject::connect(m_view, &WebContentView::maximize_window, this, [this]() { 141 m_window->showMaximized(); 142 return Gfx::IntRect { m_window->x(), m_window->y(), m_window->width(), m_window->height() }; 143 }); 144 QObject::connect(m_view, &WebContentView::minimize_window, this, [this]() { 145 m_window->showMinimized(); 146 return Gfx::IntRect { m_window->x(), m_window->y(), m_window->width(), m_window->height() }; 147 }); 148 QObject::connect(m_view, &WebContentView::fullscreen_window, this, [this]() { 149 m_window->showFullScreen(); 150 return Gfx::IntRect { m_window->x(), m_window->y(), m_window->width(), m_window->height() }; 151 }); 152} 153 154void Tab::focus_location_editor() 155{ 156 m_location_edit->setFocus(); 157 m_location_edit->selectAll(); 158} 159 160void Tab::navigate(QString url, LoadType load_type) 161{ 162 if (!url.startsWith("http://", Qt::CaseInsensitive) && !url.startsWith("https://", Qt::CaseInsensitive) && !url.startsWith("file://", Qt::CaseInsensitive) && !url.startsWith("about:", Qt::CaseInsensitive)) 163 url = "http://" + url; 164 m_is_history_navigation = (load_type == LoadType::HistoryNavigation); 165 view().load(ak_deprecated_string_from_qstring(url)); 166} 167 168void Tab::back() 169{ 170 if (!m_history.can_go_back()) 171 return; 172 173 m_is_history_navigation = true; 174 m_history.go_back(); 175 view().load(m_history.current().url.to_deprecated_string()); 176} 177 178void Tab::forward() 179{ 180 if (!m_history.can_go_forward()) 181 return; 182 183 m_is_history_navigation = true; 184 m_history.go_forward(); 185 view().load(m_history.current().url.to_deprecated_string()); 186} 187 188void Tab::home() 189{ 190 navigate(s_settings->homepage()); 191} 192 193void Tab::reload() 194{ 195 m_is_history_navigation = true; 196 view().load(m_history.current().url.to_deprecated_string()); 197} 198 199void Tab::location_edit_return_pressed() 200{ 201 navigate(m_location_edit->text()); 202} 203 204void Tab::page_title_changed(QString title) 205{ 206 m_title = title; 207 m_history.update_title(ak_deprecated_string_from_qstring(m_title)); 208 emit title_changed(tab_index(), std::move(title)); 209} 210 211void Tab::page_favicon_changed(QIcon icon) 212{ 213 emit favicon_changed(tab_index(), std::move(icon)); 214} 215 216int Tab::tab_index() 217{ 218 return m_window->tab_index(this); 219} 220 221void Tab::debug_request(DeprecatedString const& request, DeprecatedString const& argument) 222{ 223 if (request == "dump-history") 224 m_history.dump(); 225 else 226 m_view->debug_request(request, argument); 227} 228 229void Tab::resizeEvent(QResizeEvent* event) 230{ 231 QWidget::resizeEvent(event); 232 if (m_hover_label->isVisible()) 233 update_hover_label(); 234} 235 236void Tab::update_hover_label() 237{ 238 m_hover_label->resize(QFontMetrics(m_hover_label->font()).boundingRect(m_hover_label->text()).adjusted(-4, -2, 4, 2).size()); 239 m_hover_label->move(6, height() - m_hover_label->height() - 8); 240 m_hover_label->raise(); 241}