Serenity Operating System
at master 803 lines 28 kB view raw
1/* 2 * Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2021, Maciej Zygmanowski <sppmacd@pm.me> 4 * Copyright (c) 2021, Sam Atkins <atkinssj@serenityos.org> 5 * Copyright (c) 2022, the SerenityOS developers. 6 * Copyright (c) 2022, networkException <networkexception@serenityos.org> 7 * 8 * SPDX-License-Identifier: BSD-2-Clause 9 */ 10 11#include "Tab.h" 12#include "BookmarksBarWidget.h" 13#include "Browser.h" 14#include "BrowserWindow.h" 15#include "ConsoleWidget.h" 16#include "DownloadWidget.h" 17#include "History/HistoryWidget.h" 18#include "InspectorWidget.h" 19#include "StorageWidget.h" 20#include <AK/StringBuilder.h> 21#include <AK/URL.h> 22#include <Applications/Browser/TabGML.h> 23#include <LibConfig/Client.h> 24#include <LibGUI/Action.h> 25#include <LibGUI/Application.h> 26#include <LibGUI/BoxLayout.h> 27#include <LibGUI/Button.h> 28#include <LibGUI/Clipboard.h> 29#include <LibGUI/Menu.h> 30#include <LibGUI/MessageBox.h> 31#include <LibGUI/Statusbar.h> 32#include <LibGUI/TextBox.h> 33#include <LibGUI/Toolbar.h> 34#include <LibGUI/ToolbarContainer.h> 35#include <LibGUI/Window.h> 36#include <LibJS/Interpreter.h> 37#include <LibWeb/HTML/BrowsingContext.h> 38#include <LibWeb/HTML/SyntaxHighlighter/SyntaxHighlighter.h> 39#include <LibWeb/Layout/BlockContainer.h> 40#include <LibWeb/Layout/Viewport.h> 41#include <LibWeb/Loader/ResourceLoader.h> 42#include <LibWebView/OutOfProcessWebView.h> 43 44namespace Browser { 45 46Tab::~Tab() 47{ 48 close_sub_widgets(); 49} 50 51URL url_from_user_input(DeprecatedString const& input) 52{ 53 if (input.starts_with('?') && !g_search_engine.is_empty()) 54 return URL(g_search_engine.replace("{}"sv, URL::percent_encode(input.substring_view(1)), ReplaceMode::FirstOnly)); 55 56 URL url_with_http_schema = URL(DeprecatedString::formatted("http://{}", input)); 57 if (url_with_http_schema.is_valid() && url_with_http_schema.port().has_value()) 58 return url_with_http_schema; 59 60 URL url = URL(input); 61 if (url.is_valid()) 62 return url; 63 64 return url_with_http_schema; 65} 66 67void Tab::start_download(const URL& url) 68{ 69 auto window = GUI::Window::construct(&this->window()); 70 window->resize(300, 170); 71 window->set_title(DeprecatedString::formatted("0% of {}", url.basename())); 72 window->set_resizable(false); 73 (void)window->set_main_widget<DownloadWidget>(url).release_value_but_fixme_should_propagate_errors(); 74 window->show(); 75} 76 77void Tab::view_source(const URL& url, DeprecatedString const& source) 78{ 79 auto window = GUI::Window::construct(&this->window()); 80 auto editor = window->set_main_widget<GUI::TextEditor>().release_value_but_fixme_should_propagate_errors(); 81 editor->set_text(source); 82 editor->set_mode(GUI::TextEditor::ReadOnly); 83 editor->set_syntax_highlighter(make<Web::HTML::SyntaxHighlighter>()); 84 editor->set_ruler_visible(true); 85 window->resize(640, 480); 86 window->set_title(url.to_deprecated_string()); 87 window->set_icon(g_icon_bag.filetype_text); 88 window->set_window_mode(GUI::WindowMode::Modeless); 89 window->show(); 90} 91 92void Tab::update_status(Optional<DeprecatedString> text_override, i32 count_waiting) 93{ 94 if (text_override.has_value()) { 95 m_statusbar->set_text(*text_override); 96 return; 97 } 98 99 if (m_loaded) { 100 m_statusbar->set_text(""); 101 return; 102 } 103 104 VERIFY(m_navigating_url.has_value()); 105 106 if (count_waiting == 0) { 107 // ex: "Loading google.com" 108 m_statusbar->set_text(DeprecatedString::formatted("Loading {}", m_navigating_url->host())); 109 } else { 110 // ex: "google.com is waiting on 5 resources" 111 m_statusbar->set_text(DeprecatedString::formatted("{} is waiting on {} resource{}", m_navigating_url->host(), count_waiting, count_waiting == 1 ? ""sv : "s"sv)); 112 } 113} 114 115Tab::Tab(BrowserWindow& window) 116{ 117 load_from_gml(tab_gml).release_value_but_fixme_should_propagate_errors(); 118 119 m_toolbar_container = *find_descendant_of_type_named<GUI::ToolbarContainer>("toolbar_container"); 120 auto& toolbar = *find_descendant_of_type_named<GUI::Toolbar>("toolbar"); 121 122 auto& webview_container = *find_descendant_of_type_named<GUI::Widget>("webview_container"); 123 124 m_web_content_view = webview_container.add<WebView::OutOfProcessWebView>(); 125 126 auto preferred_color_scheme = Web::CSS::preferred_color_scheme_from_string(Config::read_string("Browser"sv, "Preferences"sv, "ColorScheme"sv, "auto"sv)); 127 m_web_content_view->set_preferred_color_scheme(preferred_color_scheme); 128 129 if (g_content_filters_enabled) 130 m_web_content_view->set_content_filters(g_content_filters); 131 else 132 m_web_content_view->set_content_filters({}); 133 134 m_web_content_view->set_proxy_mappings(g_proxies, g_proxy_mappings); 135 if (!g_webdriver_content_ipc_path.is_empty()) 136 enable_webdriver_mode(); 137 138 auto& go_back_button = toolbar.add_action(window.go_back_action()); 139 go_back_button.on_context_menu_request = [&](auto&) { 140 if (!m_history.can_go_back()) 141 return; 142 int i = 0; 143 m_go_back_context_menu = GUI::Menu::construct(); 144 for (auto& url : m_history.get_back_title_history()) { 145 i++; 146 m_go_back_context_menu->add_action(GUI::Action::create(url.to_deprecated_string(), g_icon_bag.filetype_html, [this, i](auto&) { go_back(i); })); 147 } 148 m_go_back_context_menu->popup(go_back_button.screen_relative_rect().bottom_left()); 149 }; 150 151 auto& go_forward_button = toolbar.add_action(window.go_forward_action()); 152 go_forward_button.on_context_menu_request = [&](auto&) { 153 if (!m_history.can_go_forward()) 154 return; 155 int i = 0; 156 m_go_forward_context_menu = GUI::Menu::construct(); 157 for (auto& url : m_history.get_forward_title_history()) { 158 i++; 159 m_go_forward_context_menu->add_action(GUI::Action::create(url.to_deprecated_string(), g_icon_bag.filetype_html, [this, i](auto&) { go_forward(i); })); 160 } 161 m_go_forward_context_menu->popup(go_forward_button.screen_relative_rect().bottom_left()); 162 }; 163 164 auto& go_home_button = toolbar.add_action(window.go_home_action()); 165 go_home_button.set_allowed_mouse_buttons_for_pressing(GUI::MouseButton::Primary | GUI::MouseButton::Middle); 166 go_home_button.on_middle_mouse_click = [&](auto) { 167 on_tab_open_request(Browser::url_from_user_input(g_home_url)); 168 }; 169 170 toolbar.add_action(window.reload_action()); 171 172 m_location_box = toolbar.add<GUI::UrlBox>(); 173 m_location_box->set_placeholder("Address"sv); 174 175 m_location_box->on_return_pressed = [this] { 176 auto url = url_from_location_bar(); 177 if (url.has_value()) 178 load(url.release_value()); 179 }; 180 181 m_location_box->on_ctrl_return_pressed = [this] { 182 auto url = url_from_location_bar(MayAppendTLD::Yes); 183 if (url.has_value()) 184 load(url.release_value()); 185 }; 186 187 m_location_box->add_custom_context_menu_action(GUI::Action::create("Paste && Go", [this](auto&) { 188 auto [data, mime_type, _] = GUI::Clipboard::the().fetch_data_and_type(); 189 if (!mime_type.starts_with("text/"sv)) 190 return; 191 auto const& paste_text = data; 192 if (paste_text.is_empty()) 193 return; 194 m_location_box->set_text(paste_text); 195 m_location_box->on_return_pressed(); 196 })); 197 198 auto bookmark_action = GUI::Action::create( 199 "Bookmark current URL", { Mod_Ctrl, Key_D }, [this](auto&) { 200 bookmark_current_url(); 201 }, 202 this); 203 204 m_bookmark_button = toolbar.add<GUI::Button>(); 205 m_bookmark_button->set_action(bookmark_action); 206 m_bookmark_button->set_button_style(Gfx::ButtonStyle::Coolbar); 207 m_bookmark_button->set_focus_policy(GUI::FocusPolicy::TabFocus); 208 m_bookmark_button->set_icon(g_icon_bag.bookmark_contour); 209 m_bookmark_button->set_fixed_size(22, 22); 210 211 view().on_load_start = [this](auto& url, bool is_redirect) { 212 m_navigating_url = url; 213 m_loaded = false; 214 215 // If we are loading due to a redirect, we replace the current history entry 216 // with the loaded URL 217 if (is_redirect) { 218 m_history.replace_current(url, title()); 219 } 220 221 update_status(); 222 223 m_location_box->set_icon(nullptr); 224 m_location_box->set_text(url.to_deprecated_string()); 225 226 // don't add to history if back or forward is pressed 227 if (!m_is_history_navigation) 228 m_history.push(url, title()); 229 m_is_history_navigation = false; 230 231 update_actions(); 232 update_bookmark_button(url.to_deprecated_string()); 233 234 if (m_dom_inspector_widget) 235 m_dom_inspector_widget->clear_dom_json(); 236 237 if (m_console_widget) 238 m_console_widget->reset(); 239 }; 240 241 view().on_load_finish = [this](auto&) { 242 m_navigating_url = {}; 243 m_loaded = true; 244 245 update_status(); 246 247 if (m_dom_inspector_widget) { 248 m_web_content_view->inspect_dom_tree(); 249 m_web_content_view->inspect_accessibility_tree(); 250 } 251 }; 252 253 view().on_navigate_back = [this]() { 254 go_back(1); 255 }; 256 257 view().on_navigate_forward = [this]() { 258 go_forward(1); 259 }; 260 261 view().on_refresh = [this]() { 262 reload(); 263 }; 264 265 view().on_link_click = [this](auto& url, auto& target, unsigned modifiers) { 266 if (target == "_blank" || modifiers == Mod_Ctrl) { 267 on_tab_open_request(url); 268 } else { 269 load(url); 270 } 271 }; 272 273 view().on_resource_status_change = [this](auto count_waiting) { 274 update_status({}, count_waiting); 275 }; 276 277 view().on_restore_window = [this]() { 278 this->window().show(); 279 this->window().move_to_front(); 280 m_web_content_view->set_system_visibility_state(true); 281 }; 282 283 view().on_reposition_window = [this](Gfx::IntPoint position) { 284 this->window().move_to(position); 285 return this->window().position(); 286 }; 287 288 view().on_resize_window = [this](Gfx::IntSize size) { 289 this->window().resize(size); 290 return this->window().size(); 291 }; 292 293 view().on_maximize_window = [this]() { 294 this->window().set_maximized(true); 295 return this->window().rect(); 296 }; 297 298 view().on_minimize_window = [this]() { 299 this->window().set_minimized(true); 300 m_web_content_view->set_system_visibility_state(false); 301 return this->window().rect(); 302 }; 303 304 view().on_fullscreen_window = [this]() { 305 this->window().set_fullscreen(true); 306 return this->window().rect(); 307 }; 308 309 m_link_context_menu = GUI::Menu::construct(); 310 auto link_default_action = GUI::Action::create("&Open", g_icon_bag.go_to, [this](auto&) { 311 view().on_link_click(m_link_context_menu_url, "", 0); 312 }); 313 m_link_context_menu->add_action(link_default_action); 314 m_link_context_menu_default_action = link_default_action; 315 m_link_context_menu->add_action(GUI::Action::create("Open in New &Tab", g_icon_bag.new_tab, [this](auto&) { 316 view().on_link_click(m_link_context_menu_url, "_blank", 0); 317 })); 318 m_link_context_menu->add_separator(); 319 m_link_context_menu->add_action(GUI::Action::create("&Copy URL", g_icon_bag.copy, [this](auto&) { 320 GUI::Clipboard::the().set_plain_text(m_link_context_menu_url.to_deprecated_string()); 321 })); 322 m_link_context_menu->add_separator(); 323 m_link_context_menu->add_action(GUI::Action::create("&Download", g_icon_bag.download, [this](auto&) { 324 start_download(m_link_context_menu_url); 325 })); 326 m_link_context_menu->add_separator(); 327 m_link_context_menu->add_action(window.inspect_dom_node_action()); 328 329 view().on_link_context_menu_request = [this](auto& url, auto screen_position) { 330 m_link_context_menu_url = url; 331 m_link_context_menu->popup(screen_position, m_link_context_menu_default_action); 332 }; 333 334 m_image_context_menu = GUI::Menu::construct(); 335 m_image_context_menu->add_action(GUI::Action::create("&Open Image", g_icon_bag.filetype_image, [this](auto&) { 336 view().on_link_click(m_image_context_menu_url, "", 0); 337 })); 338 m_image_context_menu->add_action(GUI::Action::create("Open Image in New &Tab", g_icon_bag.new_tab, [this](auto&) { 339 view().on_link_click(m_image_context_menu_url, "_blank", 0); 340 })); 341 m_image_context_menu->add_separator(); 342 m_image_context_menu->add_action(GUI::Action::create("&Copy Image", g_icon_bag.copy, [this](auto&) { 343 if (m_image_context_menu_bitmap.is_valid()) 344 GUI::Clipboard::the().set_bitmap(*m_image_context_menu_bitmap.bitmap()); 345 })); 346 m_image_context_menu->add_action(GUI::Action::create("Copy Image &URL", g_icon_bag.copy, [this](auto&) { 347 GUI::Clipboard::the().set_plain_text(m_image_context_menu_url.to_deprecated_string()); 348 })); 349 m_image_context_menu->add_separator(); 350 m_image_context_menu->add_action(GUI::Action::create("&Download", g_icon_bag.download, [this](auto&) { 351 start_download(m_image_context_menu_url); 352 })); 353 m_image_context_menu->add_separator(); 354 m_image_context_menu->add_action(window.inspect_dom_node_action()); 355 356 view().on_image_context_menu_request = [this](auto& image_url, auto screen_position, Gfx::ShareableBitmap const& shareable_bitmap) { 357 m_image_context_menu_url = image_url; 358 m_image_context_menu_bitmap = shareable_bitmap; 359 m_image_context_menu->popup(screen_position); 360 }; 361 362 view().on_link_middle_click = [this](auto& href, auto&, auto) { 363 view().on_link_click(href, "_blank", 0); 364 }; 365 366 view().on_title_change = [this](auto& title) { 367 if (title.is_null()) { 368 m_history.update_title(url().to_deprecated_string()); 369 m_title = url().to_deprecated_string(); 370 } else { 371 m_history.update_title(title); 372 m_title = title; 373 } 374 if (on_title_change) 375 on_title_change(m_title); 376 }; 377 378 view().on_favicon_change = [this](auto& icon) { 379 m_icon = icon; 380 m_location_box->set_icon(&icon); 381 if (on_favicon_change) 382 on_favicon_change(icon); 383 }; 384 385 view().on_get_all_cookies = [this](auto& url) -> Vector<Web::Cookie::Cookie> { 386 if (on_get_all_cookies) 387 return on_get_all_cookies(url); 388 return {}; 389 }; 390 391 view().on_get_named_cookie = [this](auto& url, auto& name) -> Optional<Web::Cookie::Cookie> { 392 if (on_get_named_cookie) 393 return on_get_named_cookie(url, name); 394 return {}; 395 }; 396 397 view().on_get_cookie = [this](auto& url, auto source) -> DeprecatedString { 398 if (on_get_cookie) 399 return on_get_cookie(url, source); 400 return {}; 401 }; 402 403 view().on_set_cookie = [this](auto& url, auto& cookie, auto source) { 404 if (on_set_cookie) 405 on_set_cookie(url, cookie, source); 406 }; 407 408 view().on_update_cookie = [this](auto& cookie) { 409 if (on_update_cookie) 410 on_update_cookie(cookie); 411 }; 412 413 view().on_get_source = [this](auto& url, auto& source) { 414 view_source(url, source); 415 }; 416 417 view().on_get_dom_tree = [this](auto& dom_tree) { 418 if (m_dom_inspector_widget) 419 m_dom_inspector_widget->set_dom_json(dom_tree); 420 }; 421 422 view().on_get_dom_node_properties = [this](auto node_id, auto& specified, auto& computed, auto& custom_properties, auto& node_box_sizing) { 423 m_dom_inspector_widget->set_dom_node_properties_json({ node_id }, specified, computed, custom_properties, node_box_sizing); 424 }; 425 426 view().on_get_accessibility_tree = [this](auto& accessibility_tree) { 427 if (m_dom_inspector_widget) 428 m_dom_inspector_widget->set_accessibility_json(accessibility_tree); 429 }; 430 431 view().on_js_console_new_message = [this](auto message_index) { 432 if (m_console_widget) 433 m_console_widget->notify_about_new_console_message(message_index); 434 }; 435 436 view().on_get_js_console_messages = [this](auto start_index, auto& message_types, auto& messages) { 437 if (m_console_widget) 438 m_console_widget->handle_console_messages(start_index, message_types, messages); 439 }; 440 441 auto focus_location_box_action = GUI::Action::create( 442 "Focus location box", { Mod_Ctrl, Key_L }, Key_F6, [this](auto&) { 443 m_location_box->set_focus(true); 444 m_location_box->select_current_line(); 445 }, 446 this); 447 448 m_statusbar = *find_descendant_of_type_named<GUI::Statusbar>("statusbar"); 449 450 view().on_link_hover = [this](auto& url) { 451 if (url.is_valid()) 452 update_status(url.to_deprecated_string()); 453 else 454 update_status(); 455 }; 456 457 view().on_url_drop = [this](auto& url) { 458 load(url); 459 }; 460 461 view().on_back_button = [this] { 462 if (m_history.can_go_back()) 463 go_back(); 464 }; 465 466 view().on_forward_button = [this] { 467 if (m_history.can_go_forward()) 468 go_forward(); 469 }; 470 471 view().on_close = [this] { 472 on_tab_close_request(*this); 473 }; 474 475 m_tab_context_menu = GUI::Menu::construct(); 476 m_tab_context_menu->add_action(GUI::CommonActions::make_reload_action([this](auto&) { 477 reload(); 478 })); 479 m_tab_context_menu->add_action(GUI::CommonActions::make_close_tab_action([this](auto&) { 480 on_tab_close_request(*this); 481 })); 482 m_tab_context_menu->add_action(GUI::Action::create("&Duplicate Tab", g_icon_bag.duplicate_tab, [this](auto&) { 483 on_tab_open_request(url()); 484 })); 485 m_tab_context_menu->add_action(GUI::Action::create("Close &Other Tabs", g_icon_bag.close_other_tabs, [this](auto&) { 486 on_tab_close_other_request(*this); 487 })); 488 489 m_page_context_menu = GUI::Menu::construct(); 490 m_page_context_menu->add_action(window.go_back_action()); 491 m_page_context_menu->add_action(window.go_forward_action()); 492 m_page_context_menu->add_action(window.reload_action()); 493 m_page_context_menu->add_separator(); 494 m_page_context_menu->add_action(window.copy_selection_action()); 495 m_page_context_menu->add_action(window.select_all_action()); 496 m_page_context_menu->add_separator(); 497 m_page_context_menu->add_action(window.view_source_action()); 498 m_page_context_menu->add_action(window.inspect_dom_tree_action()); 499 m_page_context_menu->add_action(window.inspect_dom_node_action()); 500 m_page_context_menu->add_separator(); 501 m_page_context_menu->add_action(window.take_visible_screenshot_action()); 502 m_page_context_menu->add_action(window.take_full_screenshot_action()); 503 view().on_context_menu_request = [&](auto screen_position) { 504 m_page_context_menu->popup(screen_position); 505 }; 506} 507 508Optional<URL> Tab::url_from_location_bar(MayAppendTLD may_append_tld) 509{ 510 if (m_location_box->text().starts_with('?') && g_search_engine.is_empty()) { 511 GUI::MessageBox::show(&this->window(), "Select a search engine in the Settings menu before searching."sv, "No search engine selected"sv, GUI::MessageBox::Type::Information); 512 return {}; 513 } 514 515 DeprecatedString text = m_location_box->text(); 516 517 StringBuilder builder; 518 builder.append(text); 519 if (may_append_tld == MayAppendTLD::Yes) { 520 // FIXME: Expand the list of top level domains. 521 if (!(text.ends_with(".com"sv) || text.ends_with(".net"sv) || text.ends_with(".org"sv))) { 522 builder.append(".com"sv); 523 } 524 } 525 auto final_text = builder.to_deprecated_string(); 526 527 auto url = url_from_user_input(final_text); 528 return url; 529} 530 531void Tab::load(const URL& url, LoadType load_type) 532{ 533 m_is_history_navigation = (load_type == LoadType::HistoryNavigation); 534 m_web_content_view->load(url); 535 m_location_box->set_focus(false); 536} 537 538URL Tab::url() const 539{ 540 return m_web_content_view->url(); 541} 542 543void Tab::reload() 544{ 545 load(url()); 546} 547 548void Tab::go_back(int steps) 549{ 550 m_history.go_back(steps); 551 update_actions(); 552 load(m_history.current().url, LoadType::HistoryNavigation); 553} 554 555void Tab::go_forward(int steps) 556{ 557 m_history.go_forward(steps); 558 update_actions(); 559 load(m_history.current().url, LoadType::HistoryNavigation); 560} 561 562void Tab::update_actions() 563{ 564 auto& window = this->window(); 565 if (this != &window.active_tab()) 566 return; 567 window.go_back_action().set_enabled(m_history.can_go_back()); 568 window.go_forward_action().set_enabled(m_history.can_go_forward()); 569} 570 571void Tab::bookmark_current_url() 572{ 573 auto url = this->url().to_deprecated_string(); 574 if (BookmarksBarWidget::the().contains_bookmark(url)) { 575 BookmarksBarWidget::the().remove_bookmark(url); 576 } else { 577 BookmarksBarWidget::the().add_bookmark(url, m_title); 578 } 579 update_bookmark_button(url); 580} 581 582void Tab::update_bookmark_button(DeprecatedString const& url) 583{ 584 if (BookmarksBarWidget::the().contains_bookmark(url)) { 585 m_bookmark_button->set_icon(g_icon_bag.bookmark_filled); 586 m_bookmark_button->set_tooltip("Remove Bookmark"); 587 } else { 588 m_bookmark_button->set_icon(g_icon_bag.bookmark_contour); 589 m_bookmark_button->set_tooltip("Add Bookmark"); 590 } 591} 592 593void Tab::did_become_active() 594{ 595 BookmarksBarWidget::the().on_bookmark_click = [this](auto& url, auto open) { 596 if (open == BookmarksBarWidget::Open::InNewTab) 597 on_tab_open_request(url); 598 else if (open == BookmarksBarWidget::Open::InNewWindow) 599 on_window_open_request(url); 600 else 601 load(url); 602 }; 603 604 BookmarksBarWidget::the().on_bookmark_hover = [this](auto&, auto& url) { 605 m_statusbar->set_text(url); 606 }; 607 608 BookmarksBarWidget::the().remove_from_parent(); 609 m_toolbar_container->add_child(BookmarksBarWidget::the()); 610 611 auto is_fullscreen = window().is_fullscreen(); 612 m_toolbar_container->set_visible(!is_fullscreen); 613 m_statusbar->set_visible(!is_fullscreen); 614 615 update_actions(); 616} 617 618void Tab::context_menu_requested(Gfx::IntPoint screen_position) 619{ 620 m_tab_context_menu->popup(screen_position); 621} 622 623void Tab::content_filters_changed() 624{ 625 if (g_content_filters_enabled) 626 m_web_content_view->set_content_filters(g_content_filters); 627 else 628 m_web_content_view->set_content_filters({}); 629} 630 631void Tab::proxy_mappings_changed() 632{ 633 m_web_content_view->set_proxy_mappings(g_proxies, g_proxy_mappings); 634} 635 636void Tab::action_entered(GUI::Action& action) 637{ 638 m_statusbar->set_override_text(action.status_tip()); 639} 640 641void Tab::action_left(GUI::Action&) 642{ 643 m_statusbar->set_override_text({}); 644} 645 646void Tab::window_position_changed(Gfx::IntPoint position) 647{ 648 m_web_content_view->set_window_position(position); 649} 650 651void Tab::window_size_changed(Gfx::IntSize size) 652{ 653 m_web_content_view->set_window_size(size); 654} 655 656BrowserWindow const& Tab::window() const 657{ 658 return static_cast<BrowserWindow const&>(*Widget::window()); 659} 660 661BrowserWindow& Tab::window() 662{ 663 return static_cast<BrowserWindow&>(*Widget::window()); 664} 665 666void Tab::show_inspector_window(Browser::Tab::InspectorTarget inspector_target) 667{ 668 if (!m_dom_inspector_widget) { 669 auto window = GUI::Window::construct(&this->window()); 670 window->set_window_mode(GUI::WindowMode::Modeless); 671 window->resize(300, 500); 672 window->set_title("Inspector"); 673 window->set_icon(g_icon_bag.inspector_object); 674 window->on_close = [&]() { 675 m_web_content_view->clear_inspected_dom_node(); 676 }; 677 m_dom_inspector_widget = window->set_main_widget<InspectorWidget>().release_value_but_fixme_should_propagate_errors(); 678 m_dom_inspector_widget->set_web_view(*m_web_content_view); 679 m_web_content_view->inspect_dom_tree(); 680 m_web_content_view->inspect_accessibility_tree(); 681 } 682 683 if (inspector_target == InspectorTarget::HoveredElement) { 684 // FIXME: Handle pseudo-elements 685 auto hovered_node = m_web_content_view->get_hovered_node_id(); 686 m_dom_inspector_widget->set_selection({ hovered_node }); 687 } else { 688 VERIFY(inspector_target == InspectorTarget::Document); 689 m_dom_inspector_widget->select_default_node(); 690 } 691 692 auto* window = m_dom_inspector_widget->window(); 693 window->show(); 694 window->move_to_front(); 695} 696 697void Tab::close_sub_widgets() 698{ 699 auto close_widget_window = [](auto& widget) { 700 if (widget) { 701 auto* window = widget->window(); 702 window->close(); 703 } 704 }; 705 close_widget_window(m_dom_inspector_widget); 706 close_widget_window(m_console_widget); 707 close_widget_window(m_storage_widget); 708} 709 710void Tab::show_console_window() 711{ 712 if (!m_console_widget) { 713 auto console_window = GUI::Window::construct(&window()); 714 console_window->resize(500, 300); 715 console_window->set_title("JS Console"); 716 console_window->set_icon(g_icon_bag.filetype_javascript); 717 m_console_widget = console_window->set_main_widget<ConsoleWidget>().release_value_but_fixme_should_propagate_errors(); 718 m_console_widget->on_js_input = [this](DeprecatedString const& js_source) { 719 m_web_content_view->js_console_input(js_source); 720 }; 721 m_console_widget->on_request_messages = [this](i32 start_index) { 722 m_web_content_view->js_console_request_messages(start_index); 723 }; 724 } 725 726 auto* window = m_console_widget->window(); 727 window->show(); 728 window->move_to_front(); 729} 730 731void Tab::show_storage_inspector() 732{ 733 if (!m_storage_widget) { 734 auto storage_window = GUI::Window::construct(&window()); 735 storage_window->resize(500, 300); 736 storage_window->set_title("Storage inspector"); 737 storage_window->set_icon(g_icon_bag.cookie); 738 m_storage_widget = storage_window->set_main_widget<StorageWidget>().release_value_but_fixme_should_propagate_errors(); 739 m_storage_widget->on_update_cookie = [this](Web::Cookie::Cookie cookie) { 740 if (on_update_cookie) 741 on_update_cookie(move(cookie)); 742 }; 743 } 744 745 if (on_get_cookies_entries) { 746 auto cookies = on_get_cookies_entries(); 747 m_storage_widget->clear_cookies(); 748 m_storage_widget->set_cookies_entries(cookies); 749 } 750 751 if (on_get_local_storage_entries) { 752 auto local_storage_entries = on_get_local_storage_entries(); 753 m_storage_widget->clear_local_storage_entries(); 754 m_storage_widget->set_local_storage_entries(local_storage_entries); 755 } 756 757 if (on_get_session_storage_entries) { 758 auto session_storage_entries = on_get_session_storage_entries(); 759 m_storage_widget->clear_session_storage_entries(); 760 m_storage_widget->set_session_storage_entries(session_storage_entries); 761 } 762 763 auto* window = m_storage_widget->window(); 764 window->show(); 765 window->move_to_front(); 766} 767 768void Tab::show_history_inspector() 769{ 770 if (!m_history_widget) { 771 auto history_window = GUI::Window::construct(&window()); 772 history_window->resize(500, 300); 773 history_window->set_title("History"); 774 history_window->set_icon(g_icon_bag.history); 775 m_history_widget = history_window->set_main_widget<HistoryWidget>().release_value_but_fixme_should_propagate_errors(); 776 } 777 778 m_history_widget->clear_history_entries(); 779 m_history_widget->set_history_entries(m_history.get_all_history_entries()); 780 781 auto* window = m_history_widget->window(); 782 window->show(); 783 window->move_to_front(); 784} 785 786void Tab::show_event(GUI::ShowEvent&) 787{ 788 m_web_content_view->set_visible(true); 789} 790 791void Tab::hide_event(GUI::HideEvent&) 792{ 793 m_web_content_view->set_visible(false); 794} 795 796void Tab::enable_webdriver_mode() 797{ 798 m_web_content_view->connect_to_webdriver(Browser::g_webdriver_content_ipc_path); 799 auto& webdriver_banner = *find_descendant_of_type_named<GUI::Widget>("webdriver_banner"); 800 webdriver_banner.set_visible(true); 801} 802 803}