PaintableBox.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  1. /*
  2. * Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/GenericShorthands.h>
  7. #include <LibUnicode/CharacterTypes.h>
  8. #include <LibWeb/DOM/Document.h>
  9. #include <LibWeb/HTML/HTMLHtmlElement.h>
  10. #include <LibWeb/Layout/BlockContainer.h>
  11. #include <LibWeb/Layout/InitialContainingBlock.h>
  12. #include <LibWeb/Painting/BackgroundPainting.h>
  13. #include <LibWeb/Painting/FilterPainting.h>
  14. #include <LibWeb/Painting/PaintableBox.h>
  15. #include <LibWeb/Painting/ShadowPainting.h>
  16. #include <LibWeb/Painting/StackingContext.h>
  17. #include <LibWeb/Platform/FontPlugin.h>
  18. namespace Web::Painting {
  19. NonnullRefPtr<PaintableBox> PaintableBox::create(Layout::Box const& layout_box)
  20. {
  21. return adopt_ref(*new PaintableBox(layout_box));
  22. }
  23. PaintableBox::PaintableBox(Layout::Box const& layout_box)
  24. : Paintable(layout_box)
  25. {
  26. }
  27. PaintableBox::~PaintableBox()
  28. {
  29. }
  30. void PaintableBox::invalidate_stacking_context()
  31. {
  32. m_stacking_context = nullptr;
  33. }
  34. PaintableWithLines::PaintableWithLines(Layout::BlockContainer const& layout_box)
  35. : PaintableBox(layout_box)
  36. {
  37. }
  38. PaintableWithLines::~PaintableWithLines()
  39. {
  40. }
  41. void PaintableBox::set_offset(Gfx::FloatPoint const& offset)
  42. {
  43. m_offset = offset;
  44. // FIXME: This const_cast is gross.
  45. const_cast<Layout::Box&>(layout_box()).did_set_rect();
  46. }
  47. void PaintableBox::set_content_size(Gfx::FloatSize const& size)
  48. {
  49. m_content_size = size;
  50. // FIXME: This const_cast is gross.
  51. const_cast<Layout::Box&>(layout_box()).did_set_rect();
  52. }
  53. Gfx::FloatPoint PaintableBox::effective_offset() const
  54. {
  55. Gfx::FloatPoint offset;
  56. if (m_containing_line_box_fragment.has_value()) {
  57. auto const& fragment = containing_block()->paint_box()->line_boxes()[m_containing_line_box_fragment->line_box_index].fragments()[m_containing_line_box_fragment->fragment_index];
  58. offset = fragment.offset();
  59. } else {
  60. offset = m_offset;
  61. }
  62. if (layout_box().computed_values().position() == CSS::Position::Relative) {
  63. auto const& inset = layout_box().box_model().inset;
  64. offset.translate_by(inset.left, inset.top);
  65. }
  66. return offset;
  67. }
  68. Gfx::FloatRect PaintableBox::compute_absolute_rect() const
  69. {
  70. Gfx::FloatRect rect { effective_offset(), content_size() };
  71. for (auto const* block = containing_block(); block && block->paintable(); block = block->paintable()->containing_block())
  72. rect.translate_by(block->paint_box()->effective_offset());
  73. return rect;
  74. }
  75. Gfx::FloatRect PaintableBox::absolute_rect() const
  76. {
  77. if (!m_absolute_rect.has_value())
  78. m_absolute_rect = compute_absolute_rect();
  79. return *m_absolute_rect;
  80. }
  81. void PaintableBox::set_containing_line_box_fragment(Optional<Layout::LineBoxFragmentCoordinate> fragment_coordinate)
  82. {
  83. m_containing_line_box_fragment = fragment_coordinate;
  84. }
  85. Painting::StackingContext* PaintableBox::enclosing_stacking_context()
  86. {
  87. for (auto* ancestor = layout_box().parent(); ancestor; ancestor = ancestor->parent()) {
  88. if (!is<Layout::Box>(ancestor))
  89. continue;
  90. auto& ancestor_box = static_cast<Layout::Box&>(const_cast<Layout::NodeWithStyle&>(*ancestor));
  91. if (auto* ancestor_paint_box = ancestor_box.paint_box(); ancestor_paint_box && ancestor_paint_box->stacking_context())
  92. return const_cast<StackingContext*>(ancestor_paint_box->stacking_context());
  93. }
  94. // We should always reach the Layout::InitialContainingBlock stacking context.
  95. VERIFY_NOT_REACHED();
  96. }
  97. void PaintableBox::paint(PaintContext& context, PaintPhase phase) const
  98. {
  99. if (!is_visible())
  100. return;
  101. auto clip_rect = computed_values().clip();
  102. auto should_clip_rect = clip_rect.is_rect() && layout_box().is_absolutely_positioned();
  103. if (phase == PaintPhase::Background) {
  104. if (should_clip_rect) {
  105. context.painter().save();
  106. auto border_box = absolute_border_box_rect();
  107. context.painter().add_clip_rect(clip_rect.to_rect().resolved(Paintable::layout_node(), border_box).to_rounded<int>());
  108. }
  109. paint_backdrop_filter(context);
  110. paint_background(context);
  111. paint_box_shadow(context);
  112. }
  113. if (phase == PaintPhase::Border) {
  114. paint_border(context);
  115. }
  116. if (phase == PaintPhase::Overlay && should_clip_rect)
  117. context.painter().restore();
  118. if (phase == PaintPhase::Overlay && layout_box().dom_node() && layout_box().document().inspected_node() == layout_box().dom_node()) {
  119. auto content_rect = absolute_rect();
  120. auto margin_box = box_model().margin_box();
  121. Gfx::FloatRect margin_rect;
  122. margin_rect.set_x(absolute_x() - margin_box.left);
  123. margin_rect.set_width(content_width() + margin_box.left + margin_box.right);
  124. margin_rect.set_y(absolute_y() - margin_box.top);
  125. margin_rect.set_height(content_height() + margin_box.top + margin_box.bottom);
  126. auto border_rect = absolute_border_box_rect();
  127. auto padding_rect = absolute_padding_box_rect();
  128. auto paint_inspector_rect = [&](Gfx::FloatRect const& rect, Color color) {
  129. context.painter().fill_rect(enclosing_int_rect(rect), Color(color).with_alpha(100));
  130. context.painter().draw_rect(enclosing_int_rect(rect), Color(color));
  131. };
  132. paint_inspector_rect(margin_rect, Color::Yellow);
  133. paint_inspector_rect(padding_rect, Color::Cyan);
  134. paint_inspector_rect(border_rect, Color::Green);
  135. paint_inspector_rect(content_rect, Color::Magenta);
  136. auto& font = Platform::FontPlugin::the().default_font();
  137. StringBuilder builder;
  138. if (layout_box().dom_node())
  139. builder.append(layout_box().dom_node()->debug_description());
  140. else
  141. builder.append(layout_box().debug_description());
  142. builder.appendff(" {}x{} @ {},{}", border_rect.width(), border_rect.height(), border_rect.x(), border_rect.y());
  143. auto size_text = builder.to_string();
  144. auto size_text_rect = border_rect;
  145. size_text_rect.set_y(border_rect.y() + border_rect.height());
  146. size_text_rect.set_top(size_text_rect.top());
  147. size_text_rect.set_width((float)font.width(size_text) + 4);
  148. size_text_rect.set_height(font.pixel_size() + 4);
  149. context.painter().fill_rect(enclosing_int_rect(size_text_rect), context.palette().color(Gfx::ColorRole::Tooltip));
  150. context.painter().draw_rect(enclosing_int_rect(size_text_rect), context.palette().threed_shadow1());
  151. context.painter().draw_text(enclosing_int_rect(size_text_rect), size_text, font, Gfx::TextAlignment::Center, context.palette().color(Gfx::ColorRole::TooltipText));
  152. }
  153. if (phase == PaintPhase::FocusOutline && layout_box().dom_node() && layout_box().dom_node()->is_element() && verify_cast<DOM::Element>(*layout_box().dom_node()).is_focused()) {
  154. // FIXME: Implement this as `outline` using :focus-visible in the default UA stylesheet to make it possible to override/disable.
  155. auto focus_outline_rect = enclosing_int_rect(absolute_border_box_rect()).inflated(4, 4);
  156. context.painter().draw_focus_rect(focus_outline_rect, context.palette().focus_outline());
  157. }
  158. }
  159. void PaintableBox::paint_border(PaintContext& context) const
  160. {
  161. auto borders_data = BordersData {
  162. .top = computed_values().border_top(),
  163. .right = computed_values().border_right(),
  164. .bottom = computed_values().border_bottom(),
  165. .left = computed_values().border_left(),
  166. };
  167. paint_all_borders(context, absolute_border_box_rect(), normalized_border_radii_data(), borders_data);
  168. }
  169. void PaintableBox::paint_backdrop_filter(PaintContext& context) const
  170. {
  171. auto& backdrop_filter = computed_values().backdrop_filter();
  172. if (!backdrop_filter.is_none())
  173. Painting::apply_backdrop_filter(context, layout_node(), absolute_border_box_rect(), normalized_border_radii_data(), backdrop_filter);
  174. }
  175. void PaintableBox::paint_background(PaintContext& context) const
  176. {
  177. // If the body's background properties were propagated to the root element, do no re-paint the body's background.
  178. if (layout_box().is_body() && document().html_element()->should_use_body_background_properties())
  179. return;
  180. Gfx::FloatRect background_rect;
  181. Color background_color = computed_values().background_color();
  182. auto* background_layers = &computed_values().background_layers();
  183. if (layout_box().is_root_element()) {
  184. // CSS 2.1 Appendix E.2: If the element is a root element, paint the background over the entire canvas.
  185. background_rect = context.viewport_rect().to_type<float>();
  186. // Section 2.11.2: If the computed value of background-image on the root element is none and its background-color is transparent,
  187. // user agents must instead propagate the computed values of the background properties from that element’s first HTML BODY child element.
  188. if (document().html_element()->should_use_body_background_properties()) {
  189. background_layers = document().background_layers();
  190. background_color = document().background_color(context.palette());
  191. }
  192. } else {
  193. background_rect = absolute_padding_box_rect();
  194. }
  195. // HACK: If the Box has a border, use the bordered_rect to paint the background.
  196. // This way if we have a border-radius there will be no gap between the filling and actual border.
  197. if (computed_values().border_top().width || computed_values().border_right().width || computed_values().border_bottom().width || computed_values().border_left().width)
  198. background_rect = absolute_border_box_rect();
  199. Painting::paint_background(context, layout_box(), background_rect, background_color, computed_values().image_rendering(), background_layers, normalized_border_radii_data());
  200. }
  201. void PaintableBox::paint_box_shadow(PaintContext& context) const
  202. {
  203. auto box_shadow_data = computed_values().box_shadow();
  204. if (box_shadow_data.is_empty())
  205. return;
  206. Vector<ShadowData> resolved_box_shadow_data;
  207. resolved_box_shadow_data.ensure_capacity(box_shadow_data.size());
  208. for (auto const& layer : box_shadow_data) {
  209. resolved_box_shadow_data.empend(
  210. layer.color,
  211. static_cast<int>(layer.offset_x.to_px(layout_box())),
  212. static_cast<int>(layer.offset_y.to_px(layout_box())),
  213. static_cast<int>(layer.blur_radius.to_px(layout_box())),
  214. static_cast<int>(layer.spread_distance.to_px(layout_box())),
  215. layer.placement == CSS::ShadowPlacement::Outer ? ShadowPlacement::Outer : ShadowPlacement::Inner);
  216. }
  217. Painting::paint_box_shadow(context, absolute_border_box_rect().to_rounded<int>(), normalized_border_radii_data(), resolved_box_shadow_data);
  218. }
  219. BorderRadiiData PaintableBox::normalized_border_radii_data(ShrinkRadiiForBorders shrink) const
  220. {
  221. auto border_radius_data = Painting::normalized_border_radii_data(layout_box(), absolute_border_box_rect(),
  222. computed_values().border_top_left_radius(),
  223. computed_values().border_top_right_radius(),
  224. computed_values().border_bottom_right_radius(),
  225. computed_values().border_bottom_left_radius());
  226. if (shrink == ShrinkRadiiForBorders::Yes)
  227. border_radius_data.shrink(computed_values().border_top().width, computed_values().border_right().width, computed_values().border_bottom().width, computed_values().border_left().width);
  228. return border_radius_data;
  229. }
  230. void PaintableBox::before_children_paint(PaintContext& context, PaintPhase phase, ShouldClipOverflow should_clip_overflow) const
  231. {
  232. if (!AK::first_is_one_of(phase, PaintPhase::Background, PaintPhase::Border, PaintPhase::Foreground))
  233. return;
  234. if (should_clip_overflow == ShouldClipOverflow::No)
  235. return;
  236. // FIXME: Support more overflow variations.
  237. auto clip_rect = absolute_padding_box_rect().to_rounded<int>();
  238. auto overflow_x = computed_values().overflow_x();
  239. auto overflow_y = computed_values().overflow_y();
  240. auto clip_overflow = [&] {
  241. if (!m_clipping_overflow) {
  242. context.painter().save();
  243. context.painter().add_clip_rect(clip_rect);
  244. m_clipping_overflow = true;
  245. }
  246. };
  247. if (overflow_x == CSS::Overflow::Hidden && overflow_y == CSS::Overflow::Hidden) {
  248. clip_overflow();
  249. }
  250. if (overflow_y == CSS::Overflow::Hidden || overflow_x == CSS::Overflow::Hidden) {
  251. auto border_radii_data = normalized_border_radii_data(ShrinkRadiiForBorders::Yes);
  252. if (border_radii_data.has_any_radius()) {
  253. auto corner_clipper = BorderRadiusCornerClipper::create(clip_rect, border_radii_data, CornerClip::Outside, BorderRadiusCornerClipper::UseCachedBitmap::No);
  254. if (corner_clipper.is_error()) {
  255. dbgln("Failed to create overflow border-radius corner clipper: {}", corner_clipper.error());
  256. return;
  257. }
  258. clip_overflow();
  259. m_overflow_corner_radius_clipper = corner_clipper.release_value();
  260. m_overflow_corner_radius_clipper->sample_under_corners(context.painter());
  261. }
  262. }
  263. }
  264. void PaintableBox::after_children_paint(PaintContext& context, PaintPhase phase, ShouldClipOverflow should_clip_overflow) const
  265. {
  266. if (!AK::first_is_one_of(phase, PaintPhase::Background, PaintPhase::Border, PaintPhase::Foreground))
  267. return;
  268. if (should_clip_overflow == ShouldClipOverflow::No)
  269. return;
  270. // FIXME: Support more overflow variations.
  271. if (m_clipping_overflow) {
  272. context.painter().restore();
  273. m_clipping_overflow = false;
  274. }
  275. if (m_overflow_corner_radius_clipper.has_value()) {
  276. m_overflow_corner_radius_clipper->blit_corner_clipping(context.painter());
  277. m_overflow_corner_radius_clipper = {};
  278. }
  279. }
  280. static void paint_cursor_if_needed(PaintContext& context, Layout::TextNode const& text_node, Layout::LineBoxFragment const& fragment)
  281. {
  282. auto const& browsing_context = text_node.browsing_context();
  283. if (!browsing_context.is_focused_context())
  284. return;
  285. if (!browsing_context.cursor_blink_state())
  286. return;
  287. if (browsing_context.cursor_position().node() != &text_node.dom_node())
  288. return;
  289. // NOTE: This checks if the cursor is before the start or after the end of the fragment. If it is at the end, after all text, it should still be painted.
  290. if (browsing_context.cursor_position().offset() < (unsigned)fragment.start() || browsing_context.cursor_position().offset() > (unsigned)(fragment.start() + fragment.length()))
  291. return;
  292. if (!fragment.layout_node().dom_node() || !fragment.layout_node().dom_node()->is_editable())
  293. return;
  294. auto fragment_rect = fragment.absolute_rect();
  295. float cursor_x = fragment_rect.x() + text_node.font().width(fragment.text().substring_view(0, text_node.browsing_context().cursor_position().offset() - fragment.start()));
  296. float cursor_top = fragment_rect.top();
  297. float cursor_height = fragment_rect.height();
  298. Gfx::IntRect cursor_rect(cursor_x, cursor_top, 1, cursor_height);
  299. context.painter().draw_rect(cursor_rect, text_node.computed_values().color());
  300. }
  301. static void paint_text_decoration(Gfx::Painter& painter, Layout::Node const& text_node, Layout::LineBoxFragment const& fragment)
  302. {
  303. auto& font = fragment.layout_node().font();
  304. auto fragment_box = enclosing_int_rect(fragment.absolute_rect());
  305. auto glyph_height = font.pixel_size();
  306. auto baseline = fragment_box.height() / 2 - (glyph_height + 4) / 2 + glyph_height;
  307. auto line_color = text_node.computed_values().text_decoration_color();
  308. int line_thickness = [&] {
  309. CSS::Length computed_thickness = text_node.computed_values().text_decoration_thickness().resolved(text_node, CSS::Length(1, CSS::Length::Type::Em));
  310. if (computed_thickness.is_auto())
  311. return max(glyph_height * 0.1f, 1.f);
  312. return computed_thickness.to_px(text_node);
  313. }();
  314. auto text_decoration_lines = text_node.computed_values().text_decoration_line();
  315. for (auto line : text_decoration_lines) {
  316. Gfx::IntPoint line_start_point {};
  317. Gfx::IntPoint line_end_point {};
  318. switch (line) {
  319. case CSS::TextDecorationLine::None:
  320. return;
  321. case CSS::TextDecorationLine::Underline:
  322. line_start_point = fragment_box.top_left().translated(0, baseline + 2);
  323. line_end_point = fragment_box.top_right().translated(0, baseline + 2);
  324. break;
  325. case CSS::TextDecorationLine::Overline:
  326. line_start_point = fragment_box.top_left().translated(0, baseline - glyph_height);
  327. line_end_point = fragment_box.top_right().translated(0, baseline - glyph_height);
  328. break;
  329. case CSS::TextDecorationLine::LineThrough: {
  330. auto x_height = font.x_height();
  331. line_start_point = fragment_box.top_left().translated(0, baseline - x_height / 2);
  332. line_end_point = fragment_box.top_right().translated(0, baseline - x_height / 2);
  333. break;
  334. }
  335. case CSS::TextDecorationLine::Blink:
  336. // Conforming user agents may simply not blink the text
  337. return;
  338. }
  339. switch (text_node.computed_values().text_decoration_style()) {
  340. case CSS::TextDecorationStyle::Solid:
  341. painter.draw_line(line_start_point, line_end_point, line_color, line_thickness, Gfx::Painter::LineStyle::Solid);
  342. break;
  343. case CSS::TextDecorationStyle::Double:
  344. switch (line) {
  345. case CSS::TextDecorationLine::Underline:
  346. break;
  347. case CSS::TextDecorationLine::Overline:
  348. line_start_point.translate_by(0, -line_thickness - 1);
  349. line_end_point.translate_by(0, -line_thickness - 1);
  350. break;
  351. case CSS::TextDecorationLine::LineThrough:
  352. line_start_point.translate_by(0, -line_thickness / 2);
  353. line_end_point.translate_by(0, -line_thickness / 2);
  354. break;
  355. default:
  356. VERIFY_NOT_REACHED();
  357. }
  358. painter.draw_line(line_start_point, line_end_point, line_color, line_thickness);
  359. painter.draw_line(line_start_point.translated(0, line_thickness + 1), line_end_point.translated(0, line_thickness + 1), line_color, line_thickness);
  360. break;
  361. case CSS::TextDecorationStyle::Dashed:
  362. painter.draw_line(line_start_point, line_end_point, line_color, line_thickness, Gfx::Painter::LineStyle::Dashed);
  363. break;
  364. case CSS::TextDecorationStyle::Dotted:
  365. painter.draw_line(line_start_point, line_end_point, line_color, line_thickness, Gfx::Painter::LineStyle::Dotted);
  366. break;
  367. case CSS::TextDecorationStyle::Wavy:
  368. painter.draw_triangle_wave(line_start_point, line_end_point, line_color, line_thickness + 1, line_thickness);
  369. break;
  370. }
  371. }
  372. }
  373. static void paint_text_fragment(PaintContext& context, Layout::TextNode const& text_node, Layout::LineBoxFragment const& fragment, Painting::PaintPhase phase)
  374. {
  375. auto& painter = context.painter();
  376. if (phase == Painting::PaintPhase::Foreground) {
  377. auto fragment_absolute_rect = fragment.absolute_rect();
  378. if (text_node.document().inspected_node() == &text_node.dom_node())
  379. context.painter().draw_rect(enclosing_int_rect(fragment_absolute_rect), Color::Magenta);
  380. // FIXME: text-transform should be done already in layout, since uppercase glyphs may be wider than lowercase, etc.
  381. auto text = text_node.text_for_rendering();
  382. auto text_transform = text_node.computed_values().text_transform();
  383. if (text_transform == CSS::TextTransform::Uppercase)
  384. text = Unicode::to_unicode_uppercase_full(text_node.text_for_rendering());
  385. if (text_transform == CSS::TextTransform::Lowercase)
  386. text = Unicode::to_unicode_lowercase_full(text_node.text_for_rendering());
  387. Gfx::FloatPoint baseline_start { fragment_absolute_rect.x(), fragment_absolute_rect.y() + fragment.baseline() };
  388. Utf8View view { text.substring_view(fragment.start(), fragment.length()) };
  389. painter.draw_text_run(baseline_start, view, fragment.layout_node().font(), text_node.computed_values().color());
  390. auto selection_rect = fragment.selection_rect(text_node.font());
  391. if (!selection_rect.is_empty()) {
  392. painter.fill_rect(enclosing_int_rect(selection_rect), context.palette().selection());
  393. Gfx::PainterStateSaver saver(painter);
  394. painter.add_clip_rect(enclosing_int_rect(selection_rect));
  395. painter.draw_text_run(baseline_start, view, fragment.layout_node().font(), context.palette().selection_text());
  396. }
  397. paint_text_decoration(painter, text_node, fragment);
  398. paint_cursor_if_needed(context, text_node, fragment);
  399. }
  400. }
  401. void PaintableWithLines::paint(PaintContext& context, PaintPhase phase) const
  402. {
  403. if (!is_visible())
  404. return;
  405. PaintableBox::paint(context, phase);
  406. if (m_line_boxes.is_empty())
  407. return;
  408. bool should_clip_overflow = computed_values().overflow_x() != CSS::Overflow::Visible && computed_values().overflow_y() != CSS::Overflow::Visible;
  409. Optional<BorderRadiusCornerClipper> corner_clipper;
  410. if (should_clip_overflow) {
  411. context.painter().save();
  412. // FIXME: Handle overflow-x and overflow-y being different values.
  413. auto clip_box = absolute_padding_box_rect().to_rounded<int>();
  414. context.painter().add_clip_rect(clip_box);
  415. auto scroll_offset = static_cast<Layout::BlockContainer const&>(layout_box()).scroll_offset();
  416. context.painter().translate(-scroll_offset.to_type<int>());
  417. auto border_radii = normalized_border_radii_data(ShrinkRadiiForBorders::Yes);
  418. if (border_radii.has_any_radius()) {
  419. auto clipper = BorderRadiusCornerClipper::create(clip_box, border_radii);
  420. if (!clipper.is_error()) {
  421. corner_clipper = clipper.release_value();
  422. corner_clipper->sample_under_corners(context.painter());
  423. }
  424. }
  425. }
  426. // Text shadows
  427. // This is yet another loop, but done here because all shadows should appear under all text.
  428. // So, we paint the shadows before painting any text.
  429. // FIXME: Find a smarter way to do this?
  430. if (phase == PaintPhase::Foreground) {
  431. for (auto& line_box : m_line_boxes) {
  432. for (auto& fragment : line_box.fragments()) {
  433. if (is<Layout::TextNode>(fragment.layout_node())) {
  434. auto& text_shadow = fragment.layout_node().computed_values().text_shadow();
  435. if (!text_shadow.is_empty()) {
  436. Vector<ShadowData> resolved_shadow_data;
  437. resolved_shadow_data.ensure_capacity(text_shadow.size());
  438. for (auto const& layer : text_shadow) {
  439. resolved_shadow_data.empend(
  440. layer.color,
  441. static_cast<int>(layer.offset_x.to_px(layout_box())),
  442. static_cast<int>(layer.offset_y.to_px(layout_box())),
  443. static_cast<int>(layer.blur_radius.to_px(layout_box())),
  444. static_cast<int>(layer.spread_distance.to_px(layout_box())),
  445. ShadowPlacement::Outer);
  446. }
  447. context.painter().set_font(fragment.layout_node().font());
  448. Painting::paint_text_shadow(context, fragment, resolved_shadow_data);
  449. }
  450. }
  451. }
  452. }
  453. }
  454. for (auto& line_box : m_line_boxes) {
  455. for (auto& fragment : line_box.fragments()) {
  456. if (context.should_show_line_box_borders()) {
  457. auto fragment_absolute_rect = fragment.absolute_rect();
  458. context.painter().draw_rect(enclosing_int_rect(fragment_absolute_rect), Color::Green);
  459. context.painter().draw_line(
  460. fragment_absolute_rect.top_left().translated(0, fragment.baseline()).to_rounded<int>(),
  461. fragment_absolute_rect.top_right().translated(0, fragment.baseline()).to_rounded<int>(), Color::Red);
  462. }
  463. if (is<Layout::TextNode>(fragment.layout_node()))
  464. paint_text_fragment(context, static_cast<Layout::TextNode const&>(fragment.layout_node()), fragment, phase);
  465. }
  466. }
  467. if (should_clip_overflow) {
  468. context.painter().restore();
  469. if (corner_clipper.has_value())
  470. corner_clipper->blit_corner_clipping(context.painter());
  471. }
  472. // FIXME: Merge this loop with the above somehow..
  473. if (phase == PaintPhase::FocusOutline) {
  474. for (auto& line_box : m_line_boxes) {
  475. for (auto& fragment : line_box.fragments()) {
  476. auto* node = fragment.layout_node().dom_node();
  477. if (!node)
  478. continue;
  479. auto* parent = node->parent_element();
  480. if (!parent)
  481. continue;
  482. if (parent->is_focused()) {
  483. // FIXME: Implement this as `outline` using :focus-visible in the default UA stylesheet to make it possible to override/disable.
  484. auto focus_outline_rect = enclosing_int_rect(fragment.absolute_rect()).inflated(4, 4);
  485. context.painter().draw_focus_rect(focus_outline_rect, context.palette().focus_outline());
  486. }
  487. }
  488. }
  489. }
  490. }
  491. bool PaintableWithLines::handle_mousewheel(Badge<EventHandler>, Gfx::IntPoint const&, unsigned, unsigned, int wheel_delta_x, int wheel_delta_y)
  492. {
  493. if (!layout_box().is_scrollable())
  494. return false;
  495. auto new_offset = layout_box().scroll_offset();
  496. new_offset.translate_by(wheel_delta_x, wheel_delta_y);
  497. const_cast<Layout::BlockContainer&>(layout_box()).set_scroll_offset(new_offset);
  498. return true;
  499. }
  500. Layout::BlockContainer const& PaintableWithLines::layout_box() const
  501. {
  502. return static_cast<Layout::BlockContainer const&>(PaintableBox::layout_box());
  503. }
  504. Layout::BlockContainer& PaintableWithLines::layout_box()
  505. {
  506. return static_cast<Layout::BlockContainer&>(PaintableBox::layout_box());
  507. }
  508. void PaintableBox::set_stacking_context(NonnullOwnPtr<StackingContext> stacking_context)
  509. {
  510. m_stacking_context = move(stacking_context);
  511. }
  512. Optional<HitTestResult> PaintableBox::hit_test(Gfx::FloatPoint const& position, HitTestType type) const
  513. {
  514. if (!is_visible())
  515. return {};
  516. if (layout_box().is_initial_containing_block_box()) {
  517. const_cast<Layout::InitialContainingBlock&>(static_cast<Layout::InitialContainingBlock const&>(layout_box())).build_stacking_context_tree_if_needed();
  518. return stacking_context()->hit_test(position, type);
  519. }
  520. if (absolute_border_box_rect().contains(position.x(), position.y()))
  521. return HitTestResult { *this };
  522. return {};
  523. }
  524. Optional<HitTestResult> PaintableWithLines::hit_test(Gfx::FloatPoint const& position, HitTestType type) const
  525. {
  526. if (!layout_box().children_are_inline())
  527. return PaintableBox::hit_test(position, type);
  528. Optional<HitTestResult> last_good_candidate;
  529. for (auto& line_box : m_line_boxes) {
  530. for (auto& fragment : line_box.fragments()) {
  531. if (is<Layout::Box>(fragment.layout_node()) && static_cast<Layout::Box const&>(fragment.layout_node()).paint_box()->stacking_context())
  532. continue;
  533. auto fragment_absolute_rect = fragment.absolute_rect();
  534. if (fragment_absolute_rect.contains(position)) {
  535. if (is<Layout::BlockContainer>(fragment.layout_node()) && fragment.layout_node().paintable())
  536. return fragment.layout_node().paintable()->hit_test(position, type);
  537. return HitTestResult { *fragment.layout_node().paintable(), fragment.text_index_at(position.x()) };
  538. }
  539. if (fragment_absolute_rect.top() <= position.y())
  540. last_good_candidate = HitTestResult { *fragment.layout_node().paintable(), fragment.text_index_at(position.x()) };
  541. }
  542. }
  543. if (type == HitTestType::TextCursor && last_good_candidate.has_value())
  544. return last_good_candidate;
  545. if (is_visible() && absolute_border_box_rect().contains(position.x(), position.y()))
  546. return HitTestResult { *this };
  547. return {};
  548. }
  549. }