BrowserWindow.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  1. /*
  2. * Copyright (c) 2022-2023, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2022, Matthew Costa <ucosty@gmail.com>
  4. * Copyright (c) 2022, Filiph Sandström <filiph.sandstrom@filfatstudios.com>
  5. * Copyright (c) 2023, Linus Groh <linusg@serenityos.org>
  6. *
  7. * SPDX-License-Identifier: BSD-2-Clause
  8. */
  9. #include "BrowserWindow.h"
  10. #include "Settings.h"
  11. #include "SettingsDialog.h"
  12. #include "Utilities.h"
  13. #include "WebContentView.h"
  14. #include <AK/TypeCasts.h>
  15. #include <Browser/CookieJar.h>
  16. #include <LibWeb/CSS/PreferredColorScheme.h>
  17. #include <LibWeb/Loader/ResourceLoader.h>
  18. #include <QAction>
  19. #include <QActionGroup>
  20. #include <QClipboard>
  21. #include <QGuiApplication>
  22. #include <QInputDialog>
  23. #include <QPlainTextEdit>
  24. #include <QTabBar>
  25. extern DeprecatedString s_serenity_resource_root;
  26. extern Browser::Settings* s_settings;
  27. BrowserWindow::BrowserWindow(Browser::CookieJar& cookie_jar, StringView webdriver_content_ipc_path)
  28. : m_cookie_jar(cookie_jar)
  29. , m_webdriver_content_ipc_path(webdriver_content_ipc_path)
  30. {
  31. m_tabs_container = new QTabWidget(this);
  32. m_tabs_container->installEventFilter(this);
  33. m_tabs_container->setElideMode(Qt::TextElideMode::ElideRight);
  34. m_tabs_container->setMovable(true);
  35. m_tabs_container->setTabsClosable(true);
  36. m_tabs_container->setDocumentMode(true);
  37. m_tabs_container->setTabBarAutoHide(true);
  38. auto* menu = menuBar()->addMenu("&File");
  39. auto* new_tab_action = new QAction("New &Tab", this);
  40. new_tab_action->setShortcuts(QKeySequence::keyBindings(QKeySequence::StandardKey::AddTab));
  41. menu->addAction(new_tab_action);
  42. auto* settings_action = new QAction("&Settings", this);
  43. settings_action->setShortcuts(QKeySequence::keyBindings(QKeySequence::StandardKey::Preferences));
  44. menu->addAction(settings_action);
  45. auto* close_current_tab_action = new QAction("Close Current Tab", this);
  46. close_current_tab_action->setShortcuts(QKeySequence::keyBindings(QKeySequence::StandardKey::Close));
  47. menu->addAction(close_current_tab_action);
  48. auto* quit_action = new QAction("&Quit", this);
  49. quit_action->setShortcuts(QKeySequence::keyBindings(QKeySequence::StandardKey::Quit));
  50. menu->addAction(quit_action);
  51. auto* edit_menu = menuBar()->addMenu("&Edit");
  52. auto* copy_action = new QAction("&Copy", this);
  53. copy_action->setShortcuts(QKeySequence::keyBindings(QKeySequence::StandardKey::Copy));
  54. edit_menu->addAction(copy_action);
  55. QObject::connect(copy_action, &QAction::triggered, this, &BrowserWindow::copy_selected_text);
  56. auto* select_all_action = new QAction("Select &All", this);
  57. select_all_action->setShortcuts(QKeySequence::keyBindings(QKeySequence::StandardKey::SelectAll));
  58. edit_menu->addAction(select_all_action);
  59. QObject::connect(select_all_action, &QAction::triggered, this, &BrowserWindow::select_all);
  60. auto* view_menu = menuBar()->addMenu("&View");
  61. auto* open_next_tab_action = new QAction("Open &Next Tab", this);
  62. open_next_tab_action->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_PageDown));
  63. view_menu->addAction(open_next_tab_action);
  64. QObject::connect(open_next_tab_action, &QAction::triggered, this, &BrowserWindow::open_next_tab);
  65. auto* open_previous_tab_action = new QAction("Open &Previous Tab", this);
  66. open_previous_tab_action->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_PageUp));
  67. view_menu->addAction(open_previous_tab_action);
  68. QObject::connect(open_previous_tab_action, &QAction::triggered, this, &BrowserWindow::open_previous_tab);
  69. view_menu->addSeparator();
  70. auto* zoom_menu = view_menu->addMenu("&Zoom");
  71. auto* zoom_in_action = new QAction("Zoom &In", this);
  72. auto zoom_in_shortcuts = QKeySequence::keyBindings(QKeySequence::StandardKey::ZoomIn);
  73. zoom_in_shortcuts.append(QKeySequence(Qt::CTRL | Qt::Key_Equal));
  74. zoom_in_action->setShortcuts(zoom_in_shortcuts);
  75. zoom_menu->addAction(zoom_in_action);
  76. QObject::connect(zoom_in_action, &QAction::triggered, this, &BrowserWindow::zoom_in);
  77. auto* zoom_out_action = new QAction("Zoom &Out", this);
  78. zoom_out_action->setShortcuts(QKeySequence::keyBindings(QKeySequence::StandardKey::ZoomOut));
  79. zoom_menu->addAction(zoom_out_action);
  80. QObject::connect(zoom_out_action, &QAction::triggered, this, &BrowserWindow::zoom_out);
  81. auto* reset_zoom_action = new QAction("&Reset Zoom", this);
  82. reset_zoom_action->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_0));
  83. zoom_menu->addAction(reset_zoom_action);
  84. QObject::connect(reset_zoom_action, &QAction::triggered, this, &BrowserWindow::reset_zoom);
  85. view_menu->addSeparator();
  86. auto* color_scheme_menu = view_menu->addMenu("&Color Scheme");
  87. auto* color_scheme_group = new QActionGroup(this);
  88. auto* auto_color_scheme = new QAction("&Auto", this);
  89. auto_color_scheme->setCheckable(true);
  90. color_scheme_group->addAction(auto_color_scheme);
  91. color_scheme_menu->addAction(auto_color_scheme);
  92. QObject::connect(auto_color_scheme, &QAction::triggered, this, &BrowserWindow::enable_auto_color_scheme);
  93. auto* light_color_scheme = new QAction("&Light", this);
  94. light_color_scheme->setCheckable(true);
  95. color_scheme_group->addAction(light_color_scheme);
  96. color_scheme_menu->addAction(light_color_scheme);
  97. QObject::connect(light_color_scheme, &QAction::triggered, this, &BrowserWindow::enable_light_color_scheme);
  98. auto* dark_color_scheme = new QAction("&Dark", this);
  99. dark_color_scheme->setCheckable(true);
  100. color_scheme_group->addAction(dark_color_scheme);
  101. color_scheme_menu->addAction(dark_color_scheme);
  102. QObject::connect(dark_color_scheme, &QAction::triggered, this, &BrowserWindow::enable_dark_color_scheme);
  103. auto_color_scheme->setChecked(true);
  104. auto* inspect_menu = menuBar()->addMenu("&Inspect");
  105. auto* view_source_action = new QAction("View &Source", this);
  106. view_source_action->setIcon(QIcon(QString("%1/res/icons/16x16/filetype-html.png").arg(s_serenity_resource_root.characters())));
  107. view_source_action->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_U));
  108. inspect_menu->addAction(view_source_action);
  109. QObject::connect(view_source_action, &QAction::triggered, this, [this] {
  110. if (m_current_tab) {
  111. m_current_tab->view().get_source();
  112. }
  113. });
  114. auto* js_console_action = new QAction("Show &JS Console", this);
  115. js_console_action->setIcon(QIcon(QString("%1/res/icons/16x16/filetype-javascript.png").arg(s_serenity_resource_root.characters())));
  116. js_console_action->setShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_J));
  117. inspect_menu->addAction(js_console_action);
  118. QObject::connect(js_console_action, &QAction::triggered, this, [this] {
  119. if (m_current_tab) {
  120. m_current_tab->view().show_js_console();
  121. }
  122. });
  123. auto* inspector_action = new QAction("Open &Inspector");
  124. inspector_action->setIcon(QIcon(QString("%1/res/icons/browser/dom-tree.png").arg(s_serenity_resource_root.characters())));
  125. inspector_action->setShortcut(QKeySequence("Ctrl+Shift+I"));
  126. inspect_menu->addAction(inspector_action);
  127. QObject::connect(inspector_action, &QAction::triggered, this, [this] {
  128. if (m_current_tab) {
  129. m_current_tab->view().show_inspector();
  130. }
  131. });
  132. auto* debug_menu = menuBar()->addMenu("&Debug");
  133. auto* dump_dom_tree_action = new QAction("Dump DOM Tree", this);
  134. dump_dom_tree_action->setIcon(QIcon(QString("%1/res/icons/browser/dom-tree.png").arg(s_serenity_resource_root.characters())));
  135. debug_menu->addAction(dump_dom_tree_action);
  136. QObject::connect(dump_dom_tree_action, &QAction::triggered, this, [this] {
  137. debug_request("dump-dom-tree");
  138. });
  139. auto* dump_layout_tree_action = new QAction("Dump Layout Tree", this);
  140. dump_layout_tree_action->setIcon(QIcon(QString("%1/res/icons/16x16/layout.png").arg(s_serenity_resource_root.characters())));
  141. debug_menu->addAction(dump_layout_tree_action);
  142. QObject::connect(dump_layout_tree_action, &QAction::triggered, this, [this] {
  143. debug_request("dump-layout-tree");
  144. });
  145. auto* dump_stacking_context_tree_action = new QAction("Dump Stacking Context Tree", this);
  146. dump_stacking_context_tree_action->setIcon(QIcon(QString("%1/res/icons/16x16/layers.png").arg(s_serenity_resource_root.characters())));
  147. debug_menu->addAction(dump_stacking_context_tree_action);
  148. QObject::connect(dump_stacking_context_tree_action, &QAction::triggered, this, [this] {
  149. debug_request("dump-stacking-context-tree");
  150. });
  151. auto* dump_style_sheets_action = new QAction("Dump Style Sheets", this);
  152. dump_style_sheets_action->setIcon(QIcon(QString("%1/res/icons/16x16/filetype-css.png").arg(s_serenity_resource_root.characters())));
  153. debug_menu->addAction(dump_style_sheets_action);
  154. QObject::connect(dump_style_sheets_action, &QAction::triggered, this, [this] {
  155. debug_request("dump-style-sheets");
  156. });
  157. auto* dump_history_action = new QAction("Dump History", this);
  158. dump_history_action->setIcon(QIcon(QString("%1/res/icons/16x16/history.png").arg(s_serenity_resource_root.characters())));
  159. debug_menu->addAction(dump_history_action);
  160. QObject::connect(dump_history_action, &QAction::triggered, this, [this] {
  161. debug_request("dump-history");
  162. });
  163. auto* dump_cookies_action = new QAction("Dump Cookies", this);
  164. dump_cookies_action->setIcon(QIcon(QString("%1/res/icons/browser/cookie.png").arg(s_serenity_resource_root.characters())));
  165. debug_menu->addAction(dump_cookies_action);
  166. QObject::connect(dump_cookies_action, &QAction::triggered, this, [this] {
  167. m_cookie_jar.dump_cookies();
  168. });
  169. auto* dump_local_storage_action = new QAction("Dump Local Storage", this);
  170. dump_local_storage_action->setIcon(QIcon(QString("%1/res/icons/browser/local-storage.png").arg(s_serenity_resource_root.characters())));
  171. debug_menu->addAction(dump_local_storage_action);
  172. QObject::connect(dump_local_storage_action, &QAction::triggered, this, [this] {
  173. debug_request("dump-local-storage");
  174. });
  175. debug_menu->addSeparator();
  176. auto* show_line_box_borders_action = new QAction("Show Line Box Borders", this);
  177. show_line_box_borders_action->setCheckable(true);
  178. debug_menu->addAction(show_line_box_borders_action);
  179. QObject::connect(show_line_box_borders_action, &QAction::triggered, this, [this, show_line_box_borders_action] {
  180. bool state = show_line_box_borders_action->isChecked();
  181. debug_request("set-line-box-borders", state ? "on" : "off");
  182. });
  183. debug_menu->addSeparator();
  184. auto* collect_garbage_action = new QAction("Collect Garbage", this);
  185. collect_garbage_action->setShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_G));
  186. collect_garbage_action->setIcon(QIcon(QString("%1/res/icons/16x16/trash-can.png").arg(s_serenity_resource_root.characters())));
  187. debug_menu->addAction(collect_garbage_action);
  188. QObject::connect(collect_garbage_action, &QAction::triggered, this, [this] {
  189. debug_request("collect-garbage");
  190. });
  191. auto* clear_cache_action = new QAction("Clear Cache", this);
  192. clear_cache_action->setIcon(QIcon(QString("%1/res/icons/browser/clear-cache.png").arg(s_serenity_resource_root.characters())));
  193. debug_menu->addAction(clear_cache_action);
  194. QObject::connect(clear_cache_action, &QAction::triggered, this, [this] {
  195. debug_request("clear-cache");
  196. });
  197. auto* spoof_user_agent_menu = debug_menu->addMenu("Spoof User Agent");
  198. spoof_user_agent_menu->setIcon(QIcon(QString("%1/res/icons/16x16/spoof.png").arg(s_serenity_resource_root.characters())));
  199. auto* user_agent_group = new QActionGroup(this);
  200. auto add_user_agent = [this, &user_agent_group, &spoof_user_agent_menu](auto& name, auto& user_agent) {
  201. auto* action = new QAction(name);
  202. action->setCheckable(true);
  203. user_agent_group->addAction(action);
  204. spoof_user_agent_menu->addAction(action);
  205. QObject::connect(action, &QAction::triggered, this, [this, user_agent] {
  206. debug_request("spoof-user-agent", user_agent);
  207. debug_request("clear-cache"); // clear the cache to ensure requests are re-done with the new user agent
  208. });
  209. return action;
  210. };
  211. auto* disable_spoofing = add_user_agent("Disabled", Web::default_user_agent);
  212. disable_spoofing->setChecked(true);
  213. add_user_agent("Chrome Linux Desktop", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36");
  214. add_user_agent("Firefox Linux Desktop", "Mozilla/5.0 (X11; Linux i686; rv:87.0) Gecko/20100101 Firefox/87.0");
  215. add_user_agent("Safari macOS Desktop", "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Safari/605.1.15");
  216. add_user_agent("Chrome Android Mobile", "Mozilla/5.0 (Linux; Android 10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.66 Mobile Safari/537.36");
  217. add_user_agent("Firefox Android Mobile", "Mozilla/5.0 (Android 11; Mobile; rv:68.0) Gecko/68.0 Firefox/86.0");
  218. add_user_agent("Safari iOS Mobile", "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1");
  219. auto* custom_user_agent_action = new QAction("Custom...");
  220. custom_user_agent_action->setCheckable(true);
  221. user_agent_group->addAction(custom_user_agent_action);
  222. spoof_user_agent_menu->addAction(custom_user_agent_action);
  223. QObject::connect(custom_user_agent_action, &QAction::triggered, this, [this, disable_spoofing] {
  224. auto user_agent = QInputDialog::getText(this, "Custom User Agent", "Enter User Agent:");
  225. if (!user_agent.isEmpty()) {
  226. debug_request("spoof-user-agent", ak_deprecated_string_from_qstring(user_agent));
  227. debug_request("clear-cache"); // clear the cache to ensure requests are re-done with the new user agent
  228. } else {
  229. disable_spoofing->activate(QAction::Trigger);
  230. }
  231. });
  232. debug_menu->addSeparator();
  233. auto* enable_scripting_action = new QAction("Enable Scripting", this);
  234. enable_scripting_action->setCheckable(true);
  235. enable_scripting_action->setChecked(true);
  236. debug_menu->addAction(enable_scripting_action);
  237. QObject::connect(enable_scripting_action, &QAction::triggered, this, [this, enable_scripting_action] {
  238. bool state = enable_scripting_action->isChecked();
  239. debug_request("scripting", state ? "on" : "off");
  240. });
  241. auto* block_pop_ups_action = new QAction("Block Pop-ups", this);
  242. block_pop_ups_action->setCheckable(true);
  243. block_pop_ups_action->setChecked(true);
  244. debug_menu->addAction(block_pop_ups_action);
  245. QObject::connect(block_pop_ups_action, &QAction::triggered, this, [this, block_pop_ups_action] {
  246. bool state = block_pop_ups_action->isChecked();
  247. debug_request("block-pop-ups", state ? "on" : "off");
  248. });
  249. auto* enable_same_origin_policy_action = new QAction("Enable Same-Origin Policy", this);
  250. enable_same_origin_policy_action->setCheckable(true);
  251. debug_menu->addAction(enable_same_origin_policy_action);
  252. QObject::connect(enable_same_origin_policy_action, &QAction::triggered, this, [this, enable_same_origin_policy_action] {
  253. bool state = enable_same_origin_policy_action->isChecked();
  254. debug_request("same-origin-policy", state ? "on" : "off");
  255. });
  256. QObject::connect(new_tab_action, &QAction::triggered, this, [this] {
  257. new_tab(s_settings->new_tab_page(), Activate::Yes);
  258. });
  259. QObject::connect(settings_action, &QAction::triggered, this, [this] {
  260. new SettingsDialog(this);
  261. });
  262. QObject::connect(quit_action, &QAction::triggered, this, &QMainWindow::close);
  263. QObject::connect(m_tabs_container, &QTabWidget::currentChanged, [this](int index) {
  264. setWindowTitle(QString("%1 - Ladybird").arg(m_tabs_container->tabText(index)));
  265. setWindowIcon(m_tabs_container->tabIcon(index));
  266. m_current_tab = verify_cast<Tab>(m_tabs_container->widget(index));
  267. });
  268. QObject::connect(m_tabs_container, &QTabWidget::tabCloseRequested, this, &BrowserWindow::close_tab);
  269. QObject::connect(close_current_tab_action, &QAction::triggered, this, &BrowserWindow::close_current_tab);
  270. new_tab(s_settings->new_tab_page(), Activate::Yes);
  271. setCentralWidget(m_tabs_container);
  272. }
  273. void BrowserWindow::debug_request(DeprecatedString const& request, DeprecatedString const& argument)
  274. {
  275. if (!m_current_tab)
  276. return;
  277. m_current_tab->debug_request(request, argument);
  278. }
  279. void BrowserWindow::new_tab(QString const& url, Activate activate)
  280. {
  281. auto tab = make<Tab>(this, m_webdriver_content_ipc_path);
  282. auto tab_ptr = tab.ptr();
  283. m_tabs.append(std::move(tab));
  284. if (m_current_tab == nullptr) {
  285. m_current_tab = tab_ptr;
  286. }
  287. m_tabs_container->addTab(tab_ptr, "New Tab");
  288. if (activate == Activate::Yes)
  289. m_tabs_container->setCurrentWidget(tab_ptr);
  290. QObject::connect(tab_ptr, &Tab::title_changed, this, &BrowserWindow::tab_title_changed);
  291. QObject::connect(tab_ptr, &Tab::favicon_changed, this, &BrowserWindow::tab_favicon_changed);
  292. QObject::connect(&tab_ptr->view(), &WebContentView::urls_dropped, this, [this](auto& urls) {
  293. VERIFY(urls.size());
  294. m_current_tab->navigate(urls[0].toString());
  295. for (qsizetype i = 1; i < urls.size(); ++i)
  296. new_tab(urls[i].toString(), Activate::No);
  297. });
  298. tab_ptr->view().on_get_all_cookies = [this](auto const& url) {
  299. return m_cookie_jar.get_all_cookies(url);
  300. };
  301. tab_ptr->view().on_get_named_cookie = [this](auto const& url, auto const& name) {
  302. return m_cookie_jar.get_named_cookie(url, name);
  303. };
  304. tab_ptr->view().on_get_cookie = [this](auto& url, auto source) -> DeprecatedString {
  305. return m_cookie_jar.get_cookie(url, source);
  306. };
  307. tab_ptr->view().on_set_cookie = [this](auto& url, auto& cookie, auto source) {
  308. m_cookie_jar.set_cookie(url, cookie, source);
  309. };
  310. tab_ptr->view().on_update_cookie = [this](auto const& cookie) {
  311. m_cookie_jar.update_cookie(cookie);
  312. };
  313. tab_ptr->focus_location_editor();
  314. // We *don't* load the initial page if we are connected to a WebDriver, as the Set URL command may come in very
  315. // quickly, and become replaced by this load.
  316. if (m_webdriver_content_ipc_path.is_empty()) {
  317. // We make it HistoryNavigation so that the initial page doesn't get added to the history.
  318. tab_ptr->navigate(url, Tab::LoadType::HistoryNavigation);
  319. }
  320. }
  321. void BrowserWindow::close_tab(int index)
  322. {
  323. auto* tab = m_tabs_container->widget(index);
  324. m_tabs_container->removeTab(index);
  325. m_tabs.remove_first_matching([&](auto& entry) {
  326. return entry == tab;
  327. });
  328. }
  329. void BrowserWindow::close_current_tab()
  330. {
  331. auto count = m_tabs_container->count() - 1;
  332. if (!count)
  333. close();
  334. else
  335. close_tab(m_tabs_container->currentIndex());
  336. }
  337. int BrowserWindow::tab_index(Tab* tab)
  338. {
  339. return m_tabs_container->indexOf(tab);
  340. }
  341. void BrowserWindow::tab_title_changed(int index, QString const& title)
  342. {
  343. if (title.isEmpty()) {
  344. m_tabs_container->setTabText(index, "...");
  345. if (m_tabs_container->currentIndex() == index)
  346. setWindowTitle("Ladybird");
  347. } else {
  348. m_tabs_container->setTabText(index, title);
  349. if (m_tabs_container->currentIndex() == index)
  350. setWindowTitle(QString("%1 - Ladybird").arg(title));
  351. }
  352. }
  353. void BrowserWindow::tab_favicon_changed(int index, QIcon icon)
  354. {
  355. m_tabs_container->setTabIcon(index, icon);
  356. if (m_tabs_container->currentIndex() == index)
  357. setWindowIcon(icon);
  358. }
  359. void BrowserWindow::open_next_tab()
  360. {
  361. if (m_tabs_container->count() <= 1)
  362. return;
  363. auto next_index = m_tabs_container->currentIndex() + 1;
  364. if (next_index >= m_tabs_container->count())
  365. next_index = 0;
  366. m_tabs_container->setCurrentIndex(next_index);
  367. }
  368. void BrowserWindow::open_previous_tab()
  369. {
  370. if (m_tabs_container->count() <= 1)
  371. return;
  372. auto next_index = m_tabs_container->currentIndex() - 1;
  373. if (next_index < 0)
  374. next_index = m_tabs_container->count() - 1;
  375. m_tabs_container->setCurrentIndex(next_index);
  376. }
  377. void BrowserWindow::enable_auto_color_scheme()
  378. {
  379. for (auto& tab : m_tabs) {
  380. tab->view().set_preferred_color_scheme(Web::CSS::PreferredColorScheme::Auto);
  381. }
  382. }
  383. void BrowserWindow::enable_light_color_scheme()
  384. {
  385. for (auto& tab : m_tabs) {
  386. tab->view().set_preferred_color_scheme(Web::CSS::PreferredColorScheme::Light);
  387. }
  388. }
  389. void BrowserWindow::enable_dark_color_scheme()
  390. {
  391. for (auto& tab : m_tabs) {
  392. tab->view().set_preferred_color_scheme(Web::CSS::PreferredColorScheme::Dark);
  393. }
  394. }
  395. void BrowserWindow::zoom_in()
  396. {
  397. if (m_current_tab)
  398. m_current_tab->view().zoom_in();
  399. }
  400. void BrowserWindow::zoom_out()
  401. {
  402. if (m_current_tab)
  403. m_current_tab->view().zoom_out();
  404. }
  405. void BrowserWindow::reset_zoom()
  406. {
  407. if (m_current_tab)
  408. m_current_tab->view().reset_zoom();
  409. }
  410. void BrowserWindow::select_all()
  411. {
  412. if (auto* tab = m_current_tab)
  413. tab->view().select_all();
  414. }
  415. void BrowserWindow::copy_selected_text()
  416. {
  417. if (auto* tab = m_current_tab) {
  418. auto text = tab->view().selected_text();
  419. auto* clipboard = QGuiApplication::clipboard();
  420. clipboard->setText(qstring_from_ak_deprecated_string(text));
  421. }
  422. }
  423. bool BrowserWindow::eventFilter(QObject* obj, QEvent* event)
  424. {
  425. if (event->type() == QEvent::MouseButtonRelease) {
  426. auto const* const mouse_event = static_cast<QMouseEvent*>(event);
  427. if (mouse_event->button() == Qt::MouseButton::MiddleButton) {
  428. if (obj == m_tabs_container) {
  429. auto const tab_index = m_tabs_container->tabBar()->tabAt(mouse_event->pos());
  430. close_tab(tab_index);
  431. return true;
  432. }
  433. }
  434. }
  435. return QMainWindow::eventFilter(obj, event);
  436. }