WebView.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. /*
  2. * Copyright (c) 2022, Dex♪ <dexes.ttp@gmail.com>
  3. * Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #define AK_DONT_REPLACE_STD
  8. #include "WebView.h"
  9. #include "RequestManagerQt.h"
  10. #include <AK/Assertions.h>
  11. #include <AK/ByteBuffer.h>
  12. #include <AK/Format.h>
  13. #include <AK/HashTable.h>
  14. #include <AK/NonnullOwnPtr.h>
  15. #include <AK/StringBuilder.h>
  16. #include <AK/Types.h>
  17. #include <LibCore/ArgsParser.h>
  18. #include <LibCore/EventLoop.h>
  19. #include <LibCore/File.h>
  20. #include <LibCore/IODevice.h>
  21. #include <LibCore/MemoryStream.h>
  22. #include <LibCore/Stream.h>
  23. #include <LibCore/System.h>
  24. #include <LibCore/Timer.h>
  25. #include <LibGfx/Bitmap.h>
  26. #include <LibGfx/Font/FontDatabase.h>
  27. #include <LibGfx/ImageDecoder.h>
  28. #include <LibGfx/PNGWriter.h>
  29. #include <LibGfx/Rect.h>
  30. #include <LibMain/Main.h>
  31. #include <LibWeb/Cookie/ParsedCookie.h>
  32. #include <LibWeb/DOM/Document.h>
  33. #include <LibWeb/HTML/BrowsingContext.h>
  34. #include <LibWeb/ImageDecoding.h>
  35. #include <LibWeb/Layout/InitialContainingBlock.h>
  36. #include <LibWeb/Loader/ResourceLoader.h>
  37. #include <LibWeb/Page/Page.h>
  38. #include <LibWeb/Painting/PaintableBox.h>
  39. #include <LibWeb/WebSockets/WebSocket.h>
  40. #include <LibWebSocket/ConnectionInfo.h>
  41. #include <LibWebSocket/Message.h>
  42. #include <LibWebSocket/WebSocket.h>
  43. #include <QCursor>
  44. #include <QIcon>
  45. #include <QMouseEvent>
  46. #include <QPaintEvent>
  47. #include <QPainter>
  48. #include <QScrollBar>
  49. #include <stdlib.h>
  50. String s_serenity_resource_root = [] {
  51. auto const* source_dir = getenv("SERENITY_SOURCE_DIR");
  52. if (source_dir) {
  53. return String::formatted("{}/Base", source_dir);
  54. }
  55. auto* home = getenv("XDG_CONFIG_HOME") ?: getenv("HOME");
  56. VERIFY(home);
  57. return String::formatted("{}/.lagom", home);
  58. }();
  59. class HeadlessBrowserPageClient final : public Web::PageClient {
  60. public:
  61. static NonnullOwnPtr<HeadlessBrowserPageClient> create(WebView& view)
  62. {
  63. return adopt_own(*new HeadlessBrowserPageClient(view));
  64. }
  65. Web::Page& page() { return *m_page; }
  66. Web::Page const& page() const { return *m_page; }
  67. Web::Layout::InitialContainingBlock* layout_root()
  68. {
  69. auto* document = page().top_level_browsing_context().active_document();
  70. if (!document)
  71. return nullptr;
  72. return document->layout_node();
  73. }
  74. void load(AK::URL const& url)
  75. {
  76. page().load(url);
  77. }
  78. void paint(Gfx::IntRect const& content_rect, Gfx::Bitmap& target)
  79. {
  80. Gfx::Painter painter(target);
  81. if (auto* document = page().top_level_browsing_context().active_document())
  82. document->update_layout();
  83. painter.fill_rect({ {}, content_rect.size() }, palette().base());
  84. auto* layout_root = this->layout_root();
  85. if (!layout_root) {
  86. return;
  87. }
  88. Web::PaintContext context(painter, palette(), content_rect.top_left());
  89. context.set_should_show_line_box_borders(false);
  90. context.set_viewport_rect(content_rect);
  91. context.set_has_focus(true);
  92. layout_root->paint_all_phases(context);
  93. }
  94. void setup_palette(Core::AnonymousBuffer theme_buffer)
  95. {
  96. m_palette_impl = Gfx::PaletteImpl::create_with_anonymous_buffer(theme_buffer);
  97. }
  98. void set_viewport_rect(Gfx::IntRect rect)
  99. {
  100. m_viewport_rect = rect;
  101. page().top_level_browsing_context().set_viewport_rect(rect);
  102. }
  103. // ^Web::PageClient
  104. virtual Gfx::Palette palette() const override
  105. {
  106. return Gfx::Palette(*m_palette_impl);
  107. }
  108. virtual Gfx::IntRect screen_rect() const override
  109. {
  110. // FIXME: Return the actual screen rect.
  111. return m_viewport_rect;
  112. }
  113. Gfx::IntRect viewport_rect() const
  114. {
  115. return m_viewport_rect;
  116. }
  117. virtual Web::CSS::PreferredColorScheme preferred_color_scheme() const override
  118. {
  119. return m_preferred_color_scheme;
  120. }
  121. virtual void page_did_change_title(String const& title) override
  122. {
  123. emit m_view.title_changed(title.characters());
  124. }
  125. virtual void page_did_set_document_in_top_level_browsing_context(Web::DOM::Document*) override
  126. {
  127. }
  128. virtual void page_did_start_loading(AK::URL const& url) override
  129. {
  130. emit m_view.loadStarted(url);
  131. }
  132. virtual void page_did_finish_loading(AK::URL const&) override
  133. {
  134. }
  135. virtual void page_did_change_selection() override
  136. {
  137. }
  138. virtual void page_did_request_cursor_change(Gfx::StandardCursor cursor) override
  139. {
  140. switch (cursor) {
  141. case Gfx::StandardCursor::Hand:
  142. m_view.setCursor(Qt::PointingHandCursor);
  143. break;
  144. case Gfx::StandardCursor::IBeam:
  145. m_view.setCursor(Qt::IBeamCursor);
  146. break;
  147. case Gfx::StandardCursor::Arrow:
  148. default:
  149. m_view.setCursor(Qt::ArrowCursor);
  150. break;
  151. }
  152. }
  153. virtual void page_did_request_context_menu(Gfx::IntPoint const&) override
  154. {
  155. }
  156. virtual void page_did_request_link_context_menu(Gfx::IntPoint const&, AK::URL const&, String const&, unsigned) override
  157. {
  158. }
  159. virtual void page_did_request_image_context_menu(Gfx::IntPoint const&, AK::URL const&, String const&, unsigned, Gfx::Bitmap const*) override
  160. {
  161. }
  162. virtual void page_did_click_link(AK::URL const&, String const&, unsigned) override
  163. {
  164. }
  165. virtual void page_did_middle_click_link(AK::URL const&, String const&, unsigned) override
  166. {
  167. }
  168. virtual void page_did_enter_tooltip_area(Gfx::IntPoint const&, String const&) override
  169. {
  170. }
  171. virtual void page_did_leave_tooltip_area() override
  172. {
  173. }
  174. virtual void page_did_hover_link(AK::URL const& url) override
  175. {
  176. emit m_view.linkHovered(url.to_string().characters());
  177. }
  178. virtual void page_did_unhover_link() override
  179. {
  180. emit m_view.linkUnhovered();
  181. }
  182. virtual void page_did_invalidate(Gfx::IntRect const&) override
  183. {
  184. m_view.viewport()->update();
  185. }
  186. virtual void page_did_change_favicon(Gfx::Bitmap const& bitmap) override
  187. {
  188. auto qimage = QImage(bitmap.scanline_u8(0), bitmap.width(), bitmap.height(), QImage::Format_ARGB32);
  189. if (qimage.isNull())
  190. return;
  191. auto qpixmap = QPixmap::fromImage(qimage);
  192. if (qpixmap.isNull())
  193. return;
  194. emit m_view.favicon_changed(QIcon(qpixmap));
  195. }
  196. virtual void page_did_layout() override
  197. {
  198. auto* layout_root = this->layout_root();
  199. VERIFY(layout_root);
  200. Gfx::IntSize content_size;
  201. if (layout_root->paint_box()->has_overflow())
  202. content_size = enclosing_int_rect(layout_root->paint_box()->scrollable_overflow_rect().value()).size();
  203. else
  204. content_size = enclosing_int_rect(layout_root->paint_box()->absolute_rect()).size();
  205. m_view.verticalScrollBar()->setMaximum(content_size.height() - m_viewport_rect.height());
  206. m_view.horizontalScrollBar()->setMaximum(content_size.width() - m_viewport_rect.width());
  207. }
  208. virtual void page_did_request_scroll_into_view(Gfx::IntRect const&) override
  209. {
  210. }
  211. virtual void page_did_request_alert(String const&) override
  212. {
  213. }
  214. virtual bool page_did_request_confirm(String const&) override
  215. {
  216. return false;
  217. }
  218. virtual String page_did_request_prompt(String const&, String const&) override
  219. {
  220. return String::empty();
  221. }
  222. virtual String page_did_request_cookie(AK::URL const&, Web::Cookie::Source) override
  223. {
  224. return String::empty();
  225. }
  226. virtual void page_did_set_cookie(AK::URL const&, Web::Cookie::ParsedCookie const&, Web::Cookie::Source) override
  227. {
  228. }
  229. void request_file(NonnullRefPtr<Web::FileRequest>& request) override
  230. {
  231. auto const file = Core::System::open(request->path(), O_RDONLY);
  232. request->on_file_request_finish(file);
  233. }
  234. private:
  235. HeadlessBrowserPageClient(WebView& view)
  236. : m_view(view)
  237. , m_page(make<Web::Page>(*this))
  238. {
  239. }
  240. WebView& m_view;
  241. NonnullOwnPtr<Web::Page> m_page;
  242. RefPtr<Gfx::PaletteImpl> m_palette_impl;
  243. Gfx::IntRect m_viewport_rect { 0, 0, 800, 600 };
  244. Web::CSS::PreferredColorScheme m_preferred_color_scheme { Web::CSS::PreferredColorScheme::Auto };
  245. };
  246. WebView::WebView()
  247. {
  248. setMouseTracking(true);
  249. m_page_client = HeadlessBrowserPageClient::create(*this);
  250. m_page_client->setup_palette(Gfx::load_system_theme(String::formatted("{}/res/themes/Default.ini", s_serenity_resource_root)));
  251. // FIXME: Allow passing these values as arguments
  252. m_page_client->set_viewport_rect({ 0, 0, 800, 600 });
  253. m_inverse_pixel_scaling_ratio = 1.0 / devicePixelRatio();
  254. }
  255. WebView::~WebView()
  256. {
  257. }
  258. void WebView::reload()
  259. {
  260. auto url = m_page_client->page().top_level_browsing_context().active_document()->url();
  261. m_page_client->load(url);
  262. }
  263. void WebView::load(String const& url)
  264. {
  265. m_page_client->load(AK::URL(url));
  266. }
  267. unsigned get_button_from_qt_event(QMouseEvent const& event)
  268. {
  269. if (event.button() == Qt::MouseButton::LeftButton)
  270. return 1;
  271. if (event.button() == Qt::MouseButton::RightButton)
  272. return 2;
  273. if (event.button() == Qt::MouseButton::MiddleButton)
  274. return 4;
  275. return 0;
  276. }
  277. unsigned get_buttons_from_qt_event(QMouseEvent const& event)
  278. {
  279. unsigned buttons = 0;
  280. if (event.buttons() & Qt::MouseButton::LeftButton)
  281. buttons |= 1;
  282. if (event.buttons() & Qt::MouseButton::RightButton)
  283. buttons |= 2;
  284. if (event.buttons() & Qt::MouseButton::MiddleButton)
  285. buttons |= 4;
  286. return buttons;
  287. }
  288. unsigned get_modifiers_from_qt_event(QMouseEvent const& event)
  289. {
  290. unsigned modifiers = 0;
  291. if (event.modifiers() & Qt::Modifier::ALT)
  292. modifiers |= 1;
  293. if (event.modifiers() & Qt::Modifier::CTRL)
  294. modifiers |= 2;
  295. if (event.modifiers() & Qt::Modifier::SHIFT)
  296. modifiers |= 4;
  297. return modifiers;
  298. }
  299. void WebView::mouseMoveEvent(QMouseEvent* event)
  300. {
  301. Gfx::IntPoint position(event->position().x() / m_inverse_pixel_scaling_ratio, event->position().y() / m_inverse_pixel_scaling_ratio);
  302. auto buttons = get_buttons_from_qt_event(*event);
  303. auto modifiers = get_modifiers_from_qt_event(*event);
  304. m_page_client->page().handle_mousemove(to_content(position), buttons, modifiers);
  305. }
  306. void WebView::mousePressEvent(QMouseEvent* event)
  307. {
  308. Gfx::IntPoint position(event->position().x() / m_inverse_pixel_scaling_ratio, event->position().y() / m_inverse_pixel_scaling_ratio);
  309. auto button = get_button_from_qt_event(*event);
  310. auto modifiers = get_modifiers_from_qt_event(*event);
  311. m_page_client->page().handle_mousedown(to_content(position), button, modifiers);
  312. }
  313. void WebView::mouseReleaseEvent(QMouseEvent* event)
  314. {
  315. Gfx::IntPoint position(event->position().x() / m_inverse_pixel_scaling_ratio, event->position().y() / m_inverse_pixel_scaling_ratio);
  316. auto button = get_button_from_qt_event(*event);
  317. auto modifiers = get_modifiers_from_qt_event(*event);
  318. m_page_client->page().handle_mouseup(to_content(position), button, modifiers);
  319. }
  320. Gfx::IntPoint WebView::to_content(Gfx::IntPoint viewport_position) const
  321. {
  322. return viewport_position.translated(horizontalScrollBar()->value(), verticalScrollBar()->value());
  323. }
  324. void WebView::paintEvent(QPaintEvent* event)
  325. {
  326. QPainter painter(viewport());
  327. painter.setClipRect(event->rect());
  328. painter.scale(m_inverse_pixel_scaling_ratio, m_inverse_pixel_scaling_ratio);
  329. auto output_rect = m_page_client->viewport_rect();
  330. output_rect.set_x(horizontalScrollBar()->value());
  331. output_rect.set_y(verticalScrollBar()->value());
  332. auto output_bitmap = MUST(Gfx::Bitmap::try_create(Gfx::BitmapFormat::BGRx8888, output_rect.size()));
  333. m_page_client->paint(output_rect, output_bitmap);
  334. QImage q_image(output_bitmap->scanline_u8(0), output_bitmap->width(), output_bitmap->height(), QImage::Format_RGB32);
  335. painter.drawImage(QPoint(0, 0), q_image);
  336. }
  337. void WebView::resizeEvent(QResizeEvent* event)
  338. {
  339. auto scaled_width = int(event->size().width() / m_inverse_pixel_scaling_ratio);
  340. auto scaled_height = int(event->size().height() / m_inverse_pixel_scaling_ratio);
  341. Gfx::IntRect rect(horizontalScrollBar()->value(), verticalScrollBar()->value(), scaled_width, scaled_height);
  342. if (verticalScrollBar()->isVisible())
  343. rect.set_width(rect.width() - verticalScrollBar()->width());
  344. if (horizontalScrollBar()->isVisible())
  345. rect.set_height(rect.height() - horizontalScrollBar()->height());
  346. m_page_client->set_viewport_rect(rect);
  347. }
  348. class HeadlessImageDecoderClient : public Web::ImageDecoding::Decoder {
  349. public:
  350. static NonnullRefPtr<HeadlessImageDecoderClient> create()
  351. {
  352. return adopt_ref(*new HeadlessImageDecoderClient());
  353. }
  354. virtual ~HeadlessImageDecoderClient() override = default;
  355. virtual Optional<Web::ImageDecoding::DecodedImage> decode_image(ReadonlyBytes data) override
  356. {
  357. auto decoder = Gfx::ImageDecoder::try_create(data);
  358. if (!decoder)
  359. return Web::ImageDecoding::DecodedImage { false, 0, Vector<Web::ImageDecoding::Frame> {} };
  360. if (!decoder->frame_count())
  361. return Web::ImageDecoding::DecodedImage { false, 0, Vector<Web::ImageDecoding::Frame> {} };
  362. Vector<Web::ImageDecoding::Frame> frames;
  363. for (size_t i = 0; i < decoder->frame_count(); ++i) {
  364. auto frame_or_error = decoder->frame(i);
  365. if (frame_or_error.is_error()) {
  366. frames.append({ {}, 0 });
  367. } else {
  368. auto frame = frame_or_error.release_value();
  369. frames.append({ move(frame.image), static_cast<size_t>(frame.duration) });
  370. }
  371. }
  372. return Web::ImageDecoding::DecodedImage {
  373. decoder->is_animated(),
  374. static_cast<u32>(decoder->loop_count()),
  375. frames,
  376. };
  377. }
  378. private:
  379. explicit HeadlessImageDecoderClient() = default;
  380. };
  381. class HeadlessWebSocketClientManager : public Web::WebSockets::WebSocketClientManager {
  382. public:
  383. class HeadlessWebSocket
  384. : public Web::WebSockets::WebSocketClientSocket
  385. , public Weakable<HeadlessWebSocket> {
  386. public:
  387. static NonnullRefPtr<HeadlessWebSocket> create(NonnullRefPtr<WebSocket::WebSocket> underlying_socket)
  388. {
  389. return adopt_ref(*new HeadlessWebSocket(move(underlying_socket)));
  390. }
  391. virtual ~HeadlessWebSocket() override
  392. {
  393. }
  394. virtual Web::WebSockets::WebSocket::ReadyState ready_state() override
  395. {
  396. switch (m_websocket->ready_state()) {
  397. case WebSocket::ReadyState::Connecting:
  398. return Web::WebSockets::WebSocket::ReadyState::Connecting;
  399. case WebSocket::ReadyState::Open:
  400. return Web::WebSockets::WebSocket::ReadyState::Open;
  401. case WebSocket::ReadyState::Closing:
  402. return Web::WebSockets::WebSocket::ReadyState::Closing;
  403. case WebSocket::ReadyState::Closed:
  404. return Web::WebSockets::WebSocket::ReadyState::Closed;
  405. }
  406. VERIFY_NOT_REACHED();
  407. }
  408. virtual void send(ByteBuffer binary_or_text_message, bool is_text) override
  409. {
  410. m_websocket->send(WebSocket::Message(binary_or_text_message, is_text));
  411. }
  412. virtual void send(StringView message) override
  413. {
  414. m_websocket->send(WebSocket::Message(message));
  415. }
  416. virtual void close(u16 code, String reason) override
  417. {
  418. m_websocket->close(code, reason);
  419. }
  420. private:
  421. HeadlessWebSocket(NonnullRefPtr<WebSocket::WebSocket> underlying_socket)
  422. : m_websocket(move(underlying_socket))
  423. {
  424. m_websocket->on_open = [weak_this = make_weak_ptr()] {
  425. if (auto strong_this = weak_this.strong_ref())
  426. if (strong_this->on_open)
  427. strong_this->on_open();
  428. };
  429. m_websocket->on_message = [weak_this = make_weak_ptr()](auto message) {
  430. if (auto strong_this = weak_this.strong_ref()) {
  431. if (strong_this->on_message) {
  432. strong_this->on_message(Web::WebSockets::WebSocketClientSocket::Message {
  433. .data = move(message.data()),
  434. .is_text = message.is_text(),
  435. });
  436. }
  437. }
  438. };
  439. m_websocket->on_error = [weak_this = make_weak_ptr()](auto error) {
  440. if (auto strong_this = weak_this.strong_ref()) {
  441. if (strong_this->on_error) {
  442. switch (error) {
  443. case WebSocket::WebSocket::Error::CouldNotEstablishConnection:
  444. strong_this->on_error(Web::WebSockets::WebSocketClientSocket::Error::CouldNotEstablishConnection);
  445. return;
  446. case WebSocket::WebSocket::Error::ConnectionUpgradeFailed:
  447. strong_this->on_error(Web::WebSockets::WebSocketClientSocket::Error::ConnectionUpgradeFailed);
  448. return;
  449. case WebSocket::WebSocket::Error::ServerClosedSocket:
  450. strong_this->on_error(Web::WebSockets::WebSocketClientSocket::Error::ServerClosedSocket);
  451. return;
  452. }
  453. VERIFY_NOT_REACHED();
  454. }
  455. }
  456. };
  457. m_websocket->on_close = [weak_this = make_weak_ptr()](u16 code, String reason, bool was_clean) {
  458. if (auto strong_this = weak_this.strong_ref())
  459. if (strong_this->on_close)
  460. strong_this->on_close(code, move(reason), was_clean);
  461. };
  462. }
  463. NonnullRefPtr<WebSocket::WebSocket> m_websocket;
  464. };
  465. static NonnullRefPtr<HeadlessWebSocketClientManager> create()
  466. {
  467. return adopt_ref(*new HeadlessWebSocketClientManager());
  468. }
  469. virtual ~HeadlessWebSocketClientManager() override { }
  470. virtual RefPtr<Web::WebSockets::WebSocketClientSocket> connect(AK::URL const& url, String const& origin) override
  471. {
  472. WebSocket::ConnectionInfo connection_info(url);
  473. connection_info.set_origin(origin);
  474. auto connection = HeadlessWebSocket::create(WebSocket::WebSocket::create(move(connection_info)));
  475. return connection;
  476. }
  477. private:
  478. HeadlessWebSocketClientManager() { }
  479. };
  480. void initialize_web_engine()
  481. {
  482. Web::ImageDecoding::Decoder::initialize(HeadlessImageDecoderClient::create());
  483. Web::ResourceLoader::initialize(RequestManagerQt::create());
  484. Web::WebSockets::WebSocketClientManager::initialize(HeadlessWebSocketClientManager::create());
  485. Web::FrameLoader::set_default_favicon_path(String::formatted("{}/res/icons/16x16/app-browser.png", s_serenity_resource_root));
  486. dbgln("Set favoicon path to {}", String::formatted("{}/res/icons/16x16/app-browser.png", s_serenity_resource_root));
  487. Gfx::FontDatabase::set_default_fonts_lookup_path(String::formatted("{}/res/fonts", s_serenity_resource_root));
  488. Gfx::FontDatabase::set_default_font_query("Katica 10 400 0");
  489. Gfx::FontDatabase::set_fixed_width_font_query("Csilla 10 400 0");
  490. Web::FrameLoader::set_error_page_url(String::formatted("file://{}/res/html/error.html", s_serenity_resource_root));
  491. }