StyleResolver.cpp 67 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2021, the SerenityOS developers.
  4. * Copyright (c) 2021, Sam Atkins <atkinssj@gmail.com>
  5. *
  6. * SPDX-License-Identifier: BSD-2-Clause
  7. */
  8. #include <AK/QuickSort.h>
  9. #include <LibWeb/CSS/CSSStyleRule.h>
  10. #include <LibWeb/CSS/Parser/DeprecatedCSSParser.h>
  11. #include <LibWeb/CSS/Parser/Parser.h>
  12. #include <LibWeb/CSS/SelectorEngine.h>
  13. #include <LibWeb/CSS/StyleResolver.h>
  14. #include <LibWeb/CSS/StyleSheet.h>
  15. #include <LibWeb/DOM/Document.h>
  16. #include <LibWeb/DOM/Element.h>
  17. #include <LibWeb/Dump.h>
  18. #include <ctype.h>
  19. #include <stdio.h>
  20. namespace Web::CSS {
  21. StyleResolver::StyleResolver(DOM::Document& document)
  22. : m_document(document)
  23. {
  24. }
  25. StyleResolver::~StyleResolver()
  26. {
  27. }
  28. static StyleSheet& default_stylesheet()
  29. {
  30. static StyleSheet* sheet;
  31. if (!sheet) {
  32. extern char const default_stylesheet_source[];
  33. String css = default_stylesheet_source;
  34. sheet = parse_css(CSS::DeprecatedParsingContext(), css).leak_ref();
  35. }
  36. return *sheet;
  37. }
  38. static StyleSheet& quirks_mode_stylesheet()
  39. {
  40. static StyleSheet* sheet;
  41. if (!sheet) {
  42. extern char const quirks_mode_stylesheet_source[];
  43. String css = quirks_mode_stylesheet_source;
  44. sheet = parse_css(CSS::DeprecatedParsingContext(), css).leak_ref();
  45. }
  46. return *sheet;
  47. }
  48. template<typename Callback>
  49. void StyleResolver::for_each_stylesheet(Callback callback) const
  50. {
  51. callback(default_stylesheet());
  52. if (document().in_quirks_mode())
  53. callback(quirks_mode_stylesheet());
  54. for (auto& sheet : document().style_sheets().sheets()) {
  55. callback(sheet);
  56. }
  57. }
  58. Vector<MatchingRule> StyleResolver::collect_matching_rules(DOM::Element const& element) const
  59. {
  60. Vector<MatchingRule> matching_rules;
  61. size_t style_sheet_index = 0;
  62. for_each_stylesheet([&](auto& sheet) {
  63. if (!is<CSSStyleSheet>(sheet))
  64. return;
  65. size_t rule_index = 0;
  66. static_cast<CSSStyleSheet const&>(sheet).for_each_effective_style_rule([&](auto& rule) {
  67. size_t selector_index = 0;
  68. for (auto& selector : rule.selectors()) {
  69. if (SelectorEngine::matches(selector, element)) {
  70. matching_rules.append({ rule, style_sheet_index, rule_index, selector_index, selector.specificity() });
  71. break;
  72. }
  73. ++selector_index;
  74. }
  75. ++rule_index;
  76. });
  77. ++style_sheet_index;
  78. });
  79. return matching_rules;
  80. }
  81. void StyleResolver::sort_matching_rules(Vector<MatchingRule>& matching_rules) const
  82. {
  83. quick_sort(matching_rules, [&](MatchingRule& a, MatchingRule& b) {
  84. auto& a_selector = a.rule->selectors()[a.selector_index];
  85. auto& b_selector = b.rule->selectors()[b.selector_index];
  86. auto a_specificity = a_selector.specificity();
  87. auto b_specificity = b_selector.specificity();
  88. if (a_selector.specificity() == b_selector.specificity()) {
  89. if (a.style_sheet_index == b.style_sheet_index)
  90. return a.rule_index < b.rule_index;
  91. return a.style_sheet_index < b.style_sheet_index;
  92. }
  93. return a_specificity < b_specificity;
  94. });
  95. }
  96. bool StyleResolver::is_inherited_property(CSS::PropertyID property_id)
  97. {
  98. static HashTable<CSS::PropertyID> inherited_properties;
  99. if (inherited_properties.is_empty()) {
  100. inherited_properties.set(CSS::PropertyID::BorderCollapse);
  101. inherited_properties.set(CSS::PropertyID::BorderSpacing);
  102. inherited_properties.set(CSS::PropertyID::Color);
  103. inherited_properties.set(CSS::PropertyID::FontFamily);
  104. inherited_properties.set(CSS::PropertyID::FontSize);
  105. inherited_properties.set(CSS::PropertyID::FontStyle);
  106. inherited_properties.set(CSS::PropertyID::FontVariant);
  107. inherited_properties.set(CSS::PropertyID::FontWeight);
  108. inherited_properties.set(CSS::PropertyID::LetterSpacing);
  109. inherited_properties.set(CSS::PropertyID::LineHeight);
  110. inherited_properties.set(CSS::PropertyID::ListStyle);
  111. inherited_properties.set(CSS::PropertyID::ListStyleImage);
  112. inherited_properties.set(CSS::PropertyID::ListStylePosition);
  113. inherited_properties.set(CSS::PropertyID::ListStyleType);
  114. inherited_properties.set(CSS::PropertyID::TextAlign);
  115. inherited_properties.set(CSS::PropertyID::TextIndent);
  116. inherited_properties.set(CSS::PropertyID::TextTransform);
  117. inherited_properties.set(CSS::PropertyID::Visibility);
  118. inherited_properties.set(CSS::PropertyID::WhiteSpace);
  119. inherited_properties.set(CSS::PropertyID::WordSpacing);
  120. // FIXME: This property is not supposed to be inherited, but we currently
  121. // rely on inheritance to propagate decorations into line boxes.
  122. inherited_properties.set(CSS::PropertyID::TextDecorationLine);
  123. }
  124. return inherited_properties.contains(property_id);
  125. }
  126. static Vector<String> split_on_whitespace(StringView const& string)
  127. {
  128. if (string.is_empty())
  129. return {};
  130. Vector<String> v;
  131. size_t substart = 0;
  132. for (size_t i = 0; i < string.length(); ++i) {
  133. char ch = string.characters_without_null_termination()[i];
  134. if (isspace(ch)) {
  135. size_t sublen = i - substart;
  136. if (sublen != 0)
  137. v.append(string.substring_view(substart, sublen));
  138. substart = i + 1;
  139. }
  140. }
  141. size_t taillen = string.length() - substart;
  142. if (taillen != 0)
  143. v.append(string.substring_view(substart, taillen));
  144. return v;
  145. }
  146. enum class Edge {
  147. Top,
  148. Right,
  149. Bottom,
  150. Left,
  151. All,
  152. };
  153. static bool contains(Edge a, Edge b)
  154. {
  155. return a == b || b == Edge::All;
  156. }
  157. static inline void set_property_border_width(StyleProperties& style, StyleValue const& value, Edge edge)
  158. {
  159. VERIFY(value.is_length());
  160. if (contains(Edge::Top, edge))
  161. style.set_property(CSS::PropertyID::BorderTopWidth, value);
  162. if (contains(Edge::Right, edge))
  163. style.set_property(CSS::PropertyID::BorderRightWidth, value);
  164. if (contains(Edge::Bottom, edge))
  165. style.set_property(CSS::PropertyID::BorderBottomWidth, value);
  166. if (contains(Edge::Left, edge))
  167. style.set_property(CSS::PropertyID::BorderLeftWidth, value);
  168. }
  169. static inline void set_property_border_color(StyleProperties& style, StyleValue const& value, Edge edge)
  170. {
  171. VERIFY(value.is_color());
  172. if (contains(Edge::Top, edge))
  173. style.set_property(CSS::PropertyID::BorderTopColor, value);
  174. if (contains(Edge::Right, edge))
  175. style.set_property(CSS::PropertyID::BorderRightColor, value);
  176. if (contains(Edge::Bottom, edge))
  177. style.set_property(CSS::PropertyID::BorderBottomColor, value);
  178. if (contains(Edge::Left, edge))
  179. style.set_property(CSS::PropertyID::BorderLeftColor, value);
  180. }
  181. static inline void set_property_border_style(StyleProperties& style, StyleValue const& value, Edge edge)
  182. {
  183. VERIFY(value.type() == CSS::StyleValue::Type::Identifier);
  184. if (contains(Edge::Top, edge))
  185. style.set_property(CSS::PropertyID::BorderTopStyle, value);
  186. if (contains(Edge::Right, edge))
  187. style.set_property(CSS::PropertyID::BorderRightStyle, value);
  188. if (contains(Edge::Bottom, edge))
  189. style.set_property(CSS::PropertyID::BorderBottomStyle, value);
  190. if (contains(Edge::Left, edge))
  191. style.set_property(CSS::PropertyID::BorderLeftStyle, value);
  192. }
  193. static inline bool is_background_repeat_property(StyleValue const& value)
  194. {
  195. if (value.is_builtin_or_dynamic())
  196. return true;
  197. switch (value.to_identifier()) {
  198. case CSS::ValueID::NoRepeat:
  199. case CSS::ValueID::Repeat:
  200. case CSS::ValueID::RepeatX:
  201. case CSS::ValueID::RepeatY:
  202. case CSS::ValueID::Round:
  203. case CSS::ValueID::Space:
  204. return true;
  205. default:
  206. return false;
  207. }
  208. }
  209. static inline bool is_color(StyleValue const& value)
  210. {
  211. if (value.is_builtin_or_dynamic())
  212. return true;
  213. if (value.is_color())
  214. return true;
  215. return false;
  216. }
  217. static inline bool is_flex_direction(StyleValue const& value)
  218. {
  219. if (value.is_builtin_or_dynamic())
  220. return true;
  221. switch (value.to_identifier()) {
  222. case ValueID::Row:
  223. case ValueID::RowReverse:
  224. case ValueID::Column:
  225. case ValueID::ColumnReverse:
  226. return true;
  227. default:
  228. return false;
  229. }
  230. }
  231. static inline bool is_flex_wrap(StyleValue const& value)
  232. {
  233. if (value.is_builtin_or_dynamic())
  234. return true;
  235. switch (value.to_identifier()) {
  236. case ValueID::Wrap:
  237. case ValueID::Nowrap:
  238. case ValueID::WrapReverse:
  239. return true;
  240. default:
  241. return false;
  242. }
  243. }
  244. static inline bool is_flex_grow_or_shrink(StyleValue const& value)
  245. {
  246. if (value.is_builtin_or_dynamic())
  247. return true;
  248. if (value.is_numeric())
  249. return true;
  250. return false;
  251. }
  252. static inline bool is_flex_basis(StyleValue const& value)
  253. {
  254. if (value.is_builtin_or_dynamic())
  255. return true;
  256. if (value.is_length())
  257. return true;
  258. if (value.is_identifier() && value.to_identifier() == ValueID::Content)
  259. return true;
  260. return false;
  261. }
  262. static inline bool is_line_style(StyleValue const& value)
  263. {
  264. if (value.is_builtin_or_dynamic())
  265. return true;
  266. switch (value.to_identifier()) {
  267. case ValueID::Dotted:
  268. case ValueID::Dashed:
  269. case ValueID::Solid:
  270. case ValueID::Double:
  271. case ValueID::Groove:
  272. case ValueID::Ridge:
  273. case ValueID::None:
  274. case ValueID::Hidden:
  275. case ValueID::Inset:
  276. case ValueID::Outset:
  277. return true;
  278. default:
  279. return false;
  280. }
  281. }
  282. static inline bool is_line_width(StyleValue const& value)
  283. {
  284. if (value.is_builtin_or_dynamic())
  285. return true;
  286. if (value.is_length())
  287. return true;
  288. // FIXME: Implement thin/medium/thick
  289. switch (value.to_identifier()) {
  290. case ValueID::None:
  291. return true;
  292. default:
  293. return false;
  294. }
  295. }
  296. static void set_property_expanding_shorthands(StyleProperties& style, CSS::PropertyID property_id, StyleValue const& value, DOM::Document& document, bool is_internally_generated_pseudo_property = false)
  297. {
  298. CSS::DeprecatedParsingContext deprecated_context(document);
  299. CSS::ParsingContext context(document);
  300. if (is_pseudo_property(property_id) && !is_internally_generated_pseudo_property) {
  301. dbgln("Ignoring non-internally-generated pseudo property: {}", string_from_property_id(property_id));
  302. return;
  303. }
  304. if (property_id == CSS::PropertyID::TextDecoration) {
  305. switch (value.to_identifier()) {
  306. case CSS::ValueID::None:
  307. case CSS::ValueID::Underline:
  308. case CSS::ValueID::Overline:
  309. case CSS::ValueID::LineThrough:
  310. case CSS::ValueID::Blink:
  311. set_property_expanding_shorthands(style, CSS::PropertyID::TextDecorationLine, value, document);
  312. break;
  313. default:
  314. break;
  315. }
  316. return;
  317. }
  318. if (property_id == CSS::PropertyID::Overflow) {
  319. style.set_property(CSS::PropertyID::OverflowX, value);
  320. style.set_property(CSS::PropertyID::OverflowY, value);
  321. return;
  322. }
  323. if (property_id == CSS::PropertyID::Border) {
  324. set_property_expanding_shorthands(style, CSS::PropertyID::BorderTop, value, document);
  325. set_property_expanding_shorthands(style, CSS::PropertyID::BorderRight, value, document);
  326. set_property_expanding_shorthands(style, CSS::PropertyID::BorderBottom, value, document);
  327. set_property_expanding_shorthands(style, CSS::PropertyID::BorderLeft, value, document);
  328. return;
  329. }
  330. if (property_id == CSS::PropertyID::BorderRadius) {
  331. // FIXME: Allow for two values per corner to support elliptical radii.
  332. // FIXME: Add support the '/' to specify elliptical radii.
  333. if (value.is_length()) {
  334. style.set_property(CSS::PropertyID::BorderTopLeftRadius, value);
  335. style.set_property(CSS::PropertyID::BorderTopRightRadius, value);
  336. style.set_property(CSS::PropertyID::BorderBottomRightRadius, value);
  337. style.set_property(CSS::PropertyID::BorderBottomLeftRadius, value);
  338. return;
  339. }
  340. // FIXME: Remove string parsing once DeprecatedCSSParser is gone.
  341. if (value.is_string()) {
  342. auto parts = split_on_whitespace(value.to_string());
  343. if (parts.size() == 2) {
  344. auto diagonal1 = parse_css_value(deprecated_context, parts[0]);
  345. auto diagonal2 = parse_css_value(deprecated_context, parts[1]);
  346. if (diagonal1 && diagonal2) {
  347. style.set_property(CSS::PropertyID::BorderTopLeftRadius, *diagonal1);
  348. style.set_property(CSS::PropertyID::BorderBottomRightRadius, *diagonal1);
  349. style.set_property(CSS::PropertyID::BorderTopRightRadius, *diagonal2);
  350. style.set_property(CSS::PropertyID::BorderBottomLeftRadius, *diagonal2);
  351. }
  352. return;
  353. }
  354. if (parts.size() == 3) {
  355. auto top_left = parse_css_value(deprecated_context, parts[0]);
  356. auto diagonal = parse_css_value(deprecated_context, parts[1]);
  357. auto bottom_right = parse_css_value(deprecated_context, parts[2]);
  358. if (top_left && diagonal && bottom_right) {
  359. style.set_property(CSS::PropertyID::BorderTopLeftRadius, *top_left);
  360. style.set_property(CSS::PropertyID::BorderBottomRightRadius, *bottom_right);
  361. style.set_property(CSS::PropertyID::BorderTopRightRadius, *diagonal);
  362. style.set_property(CSS::PropertyID::BorderBottomLeftRadius, *diagonal);
  363. }
  364. return;
  365. }
  366. if (parts.size() == 4) {
  367. auto top_left = parse_css_value(deprecated_context, parts[0]);
  368. auto top_right = parse_css_value(deprecated_context, parts[1]);
  369. auto bottom_right = parse_css_value(deprecated_context, parts[2]);
  370. auto bottom_left = parse_css_value(deprecated_context, parts[3]);
  371. if (top_left && top_right && bottom_right && bottom_left) {
  372. style.set_property(CSS::PropertyID::BorderTopLeftRadius, *top_left);
  373. style.set_property(CSS::PropertyID::BorderBottomRightRadius, *bottom_right);
  374. style.set_property(CSS::PropertyID::BorderTopRightRadius, *top_right);
  375. style.set_property(CSS::PropertyID::BorderBottomLeftRadius, *bottom_left);
  376. }
  377. return;
  378. }
  379. dbgln("Unsure what to do with CSS border-radius value '{}'", value.to_string());
  380. return;
  381. }
  382. if (value.is_value_list()) {
  383. auto& parts = static_cast<CSS::ValueListStyleValue const&>(value).values();
  384. if (parts.size() == 2) {
  385. auto diagonal1 = Parser::parse_css_value(context, property_id, parts[0]);
  386. auto diagonal2 = Parser::parse_css_value(context, property_id, parts[1]);
  387. if (diagonal1 && diagonal2) {
  388. style.set_property(CSS::PropertyID::BorderTopLeftRadius, *diagonal1);
  389. style.set_property(CSS::PropertyID::BorderBottomRightRadius, *diagonal1);
  390. style.set_property(CSS::PropertyID::BorderTopRightRadius, *diagonal2);
  391. style.set_property(CSS::PropertyID::BorderBottomLeftRadius, *diagonal2);
  392. }
  393. return;
  394. }
  395. if (parts.size() == 3) {
  396. auto top_left = Parser::parse_css_value(context, property_id, parts[0]);
  397. auto diagonal = Parser::parse_css_value(context, property_id, parts[1]);
  398. auto bottom_right = Parser::parse_css_value(context, property_id, parts[2]);
  399. if (top_left && diagonal && bottom_right) {
  400. style.set_property(CSS::PropertyID::BorderTopLeftRadius, *top_left);
  401. style.set_property(CSS::PropertyID::BorderBottomRightRadius, *bottom_right);
  402. style.set_property(CSS::PropertyID::BorderTopRightRadius, *diagonal);
  403. style.set_property(CSS::PropertyID::BorderBottomLeftRadius, *diagonal);
  404. }
  405. return;
  406. }
  407. if (parts.size() == 4) {
  408. auto top_left = Parser::parse_css_value(context, property_id, parts[0]);
  409. auto top_right = Parser::parse_css_value(context, property_id, parts[1]);
  410. auto bottom_right = Parser::parse_css_value(context, property_id, parts[2]);
  411. auto bottom_left = Parser::parse_css_value(context, property_id, parts[3]);
  412. if (top_left && top_right && bottom_right && bottom_left) {
  413. style.set_property(CSS::PropertyID::BorderTopLeftRadius, *top_left);
  414. style.set_property(CSS::PropertyID::BorderBottomRightRadius, *bottom_right);
  415. style.set_property(CSS::PropertyID::BorderTopRightRadius, *top_right);
  416. style.set_property(CSS::PropertyID::BorderBottomLeftRadius, *bottom_left);
  417. }
  418. return;
  419. }
  420. dbgln("Unsure what to do with CSS border-radius value '{}'", value.to_string());
  421. return;
  422. }
  423. return;
  424. }
  425. if (property_id == CSS::PropertyID::BorderTop
  426. || property_id == CSS::PropertyID::BorderRight
  427. || property_id == CSS::PropertyID::BorderBottom
  428. || property_id == CSS::PropertyID::BorderLeft) {
  429. Edge edge = Edge::All;
  430. switch (property_id) {
  431. case CSS::PropertyID::BorderTop:
  432. edge = Edge::Top;
  433. break;
  434. case CSS::PropertyID::BorderRight:
  435. edge = Edge::Right;
  436. break;
  437. case CSS::PropertyID::BorderBottom:
  438. edge = Edge::Bottom;
  439. break;
  440. case CSS::PropertyID::BorderLeft:
  441. edge = Edge::Left;
  442. break;
  443. default:
  444. break;
  445. }
  446. auto parts = split_on_whitespace(value.to_string());
  447. if (value.is_length()) {
  448. set_property_border_width(style, value, edge);
  449. return;
  450. }
  451. if (value.is_color()) {
  452. set_property_border_color(style, value, edge);
  453. return;
  454. }
  455. // FIXME: Remove string parsing once DeprecatedCSSParser is gone.
  456. if (value.is_string()) {
  457. auto parts = split_on_whitespace(value.to_string());
  458. if (parts.size() == 1) {
  459. if (auto value = parse_line_style(deprecated_context, parts[0])) {
  460. set_property_border_style(style, value.release_nonnull(), edge);
  461. set_property_border_color(style, ColorStyleValue::create(Gfx::Color::Black), edge);
  462. set_property_border_width(style, LengthStyleValue::create(Length(3, Length::Type::Px)), edge);
  463. return;
  464. }
  465. }
  466. RefPtr<LengthStyleValue> line_width_value;
  467. RefPtr<ColorStyleValue> color_value;
  468. RefPtr<IdentifierStyleValue> line_style_value;
  469. for (auto& part : parts) {
  470. if (auto value = parse_line_width(deprecated_context, part)) {
  471. if (line_width_value)
  472. return;
  473. line_width_value = move(value);
  474. continue;
  475. }
  476. if (auto value = parse_color(deprecated_context, part)) {
  477. if (color_value)
  478. return;
  479. color_value = move(value);
  480. continue;
  481. }
  482. if (auto value = parse_line_style(deprecated_context, part)) {
  483. if (line_style_value)
  484. return;
  485. line_style_value = move(value);
  486. continue;
  487. }
  488. }
  489. if (line_width_value)
  490. set_property_border_width(style, line_width_value.release_nonnull(), edge);
  491. if (color_value)
  492. set_property_border_color(style, color_value.release_nonnull(), edge);
  493. if (line_style_value)
  494. set_property_border_style(style, line_style_value.release_nonnull(), edge);
  495. return;
  496. }
  497. if (value.is_value_list()) {
  498. auto& parts = static_cast<CSS::ValueListStyleValue const&>(value).values();
  499. if (parts.size() == 1) {
  500. auto value = Parser::parse_css_value(context, property_id, parts[0]);
  501. if (value && is_line_style(*value)) {
  502. set_property_border_style(style, value.release_nonnull(), edge);
  503. set_property_border_color(style, ColorStyleValue::create(Gfx::Color::Black), edge);
  504. set_property_border_width(style, LengthStyleValue::create(Length(3, Length::Type::Px)), edge);
  505. return;
  506. }
  507. }
  508. RefPtr<StyleValue> line_width_value;
  509. RefPtr<StyleValue> color_value;
  510. RefPtr<StyleValue> line_style_value;
  511. for (auto& part : parts) {
  512. auto value = Parser::parse_css_value(context, property_id, part);
  513. if (!value)
  514. return;
  515. if (is_line_width(*value)) {
  516. if (line_width_value)
  517. return;
  518. line_width_value = move(value);
  519. continue;
  520. }
  521. if (is_color(*value)) {
  522. if (color_value)
  523. return;
  524. color_value = move(value);
  525. continue;
  526. }
  527. if (is_line_style(*value)) {
  528. if (line_style_value)
  529. return;
  530. line_style_value = move(value);
  531. continue;
  532. }
  533. }
  534. if (line_width_value)
  535. set_property_border_width(style, line_width_value.release_nonnull(), edge);
  536. if (color_value)
  537. set_property_border_color(style, color_value.release_nonnull(), edge);
  538. if (line_style_value)
  539. set_property_border_style(style, line_style_value.release_nonnull(), edge);
  540. return;
  541. }
  542. return;
  543. }
  544. if (property_id == CSS::PropertyID::BorderStyle) {
  545. // FIXME: Remove string parsing once DeprecatedCSSParser is gone.
  546. if (value.is_string()) {
  547. auto parts = split_on_whitespace(value.to_string());
  548. if (parts.size() == 4) {
  549. auto top = parse_css_value(deprecated_context, parts[0]);
  550. auto right = parse_css_value(deprecated_context, parts[1]);
  551. auto bottom = parse_css_value(deprecated_context, parts[2]);
  552. auto left = parse_css_value(deprecated_context, parts[3]);
  553. if (top && right && bottom && left) {
  554. style.set_property(CSS::PropertyID::BorderTopStyle, *top);
  555. style.set_property(CSS::PropertyID::BorderRightStyle, *right);
  556. style.set_property(CSS::PropertyID::BorderBottomStyle, *bottom);
  557. style.set_property(CSS::PropertyID::BorderLeftStyle, *left);
  558. }
  559. } else if (parts.size() == 3) {
  560. auto top = parse_css_value(deprecated_context, parts[0]);
  561. auto right = parse_css_value(deprecated_context, parts[1]);
  562. auto bottom = parse_css_value(deprecated_context, parts[2]);
  563. auto left = parse_css_value(deprecated_context, parts[1]);
  564. if (top && right && bottom && left) {
  565. style.set_property(CSS::PropertyID::BorderTopStyle, *top);
  566. style.set_property(CSS::PropertyID::BorderRightStyle, *right);
  567. style.set_property(CSS::PropertyID::BorderBottomStyle, *bottom);
  568. style.set_property(CSS::PropertyID::BorderLeftStyle, *left);
  569. }
  570. } else if (parts.size() == 2) {
  571. auto vertical = parse_css_value(deprecated_context, parts[0]);
  572. auto horizontal = parse_css_value(deprecated_context, parts[1]);
  573. if (vertical && horizontal) {
  574. style.set_property(CSS::PropertyID::BorderTopStyle, *vertical);
  575. style.set_property(CSS::PropertyID::BorderRightStyle, *horizontal);
  576. style.set_property(CSS::PropertyID::BorderBottomStyle, *vertical);
  577. style.set_property(CSS::PropertyID::BorderLeftStyle, *horizontal);
  578. }
  579. } else {
  580. style.set_property(CSS::PropertyID::BorderTopStyle, value);
  581. style.set_property(CSS::PropertyID::BorderRightStyle, value);
  582. style.set_property(CSS::PropertyID::BorderBottomStyle, value);
  583. style.set_property(CSS::PropertyID::BorderLeftStyle, value);
  584. }
  585. return;
  586. }
  587. if (value.is_value_list()) {
  588. auto& parts = static_cast<CSS::ValueListStyleValue const&>(value).values();
  589. if (parts.size() == 4) {
  590. auto top = Parser::parse_css_value(context, property_id, parts[0]);
  591. auto right = Parser::parse_css_value(context, property_id, parts[1]);
  592. auto bottom = Parser::parse_css_value(context, property_id, parts[2]);
  593. auto left = Parser::parse_css_value(context, property_id, parts[3]);
  594. if (top && right && bottom && left) {
  595. style.set_property(CSS::PropertyID::BorderTopStyle, *top);
  596. style.set_property(CSS::PropertyID::BorderRightStyle, *right);
  597. style.set_property(CSS::PropertyID::BorderBottomStyle, *bottom);
  598. style.set_property(CSS::PropertyID::BorderLeftStyle, *left);
  599. }
  600. } else if (parts.size() == 3) {
  601. auto top = Parser::parse_css_value(context, property_id, parts[0]);
  602. auto right = Parser::parse_css_value(context, property_id, parts[1]);
  603. auto bottom = Parser::parse_css_value(context, property_id, parts[2]);
  604. auto left = Parser::parse_css_value(context, property_id, parts[1]);
  605. if (top && right && bottom && left) {
  606. style.set_property(CSS::PropertyID::BorderTopStyle, *top);
  607. style.set_property(CSS::PropertyID::BorderRightStyle, *right);
  608. style.set_property(CSS::PropertyID::BorderBottomStyle, *bottom);
  609. style.set_property(CSS::PropertyID::BorderLeftStyle, *left);
  610. }
  611. } else if (parts.size() == 2) {
  612. auto vertical = Parser::parse_css_value(context, property_id, parts[0]);
  613. auto horizontal = Parser::parse_css_value(context, property_id, parts[1]);
  614. if (vertical && horizontal) {
  615. style.set_property(CSS::PropertyID::BorderTopStyle, *vertical);
  616. style.set_property(CSS::PropertyID::BorderRightStyle, *horizontal);
  617. style.set_property(CSS::PropertyID::BorderBottomStyle, *vertical);
  618. style.set_property(CSS::PropertyID::BorderLeftStyle, *horizontal);
  619. }
  620. } else {
  621. auto line_style = Parser::parse_css_value(context, property_id, parts[0]);
  622. if (line_style) {
  623. style.set_property(CSS::PropertyID::BorderTopStyle, *line_style);
  624. style.set_property(CSS::PropertyID::BorderRightStyle, *line_style);
  625. style.set_property(CSS::PropertyID::BorderBottomStyle, *line_style);
  626. style.set_property(CSS::PropertyID::BorderLeftStyle, *line_style);
  627. }
  628. }
  629. return;
  630. }
  631. style.set_property(CSS::PropertyID::BorderTopStyle, value);
  632. style.set_property(CSS::PropertyID::BorderRightStyle, value);
  633. style.set_property(CSS::PropertyID::BorderBottomStyle, value);
  634. style.set_property(CSS::PropertyID::BorderLeftStyle, value);
  635. return;
  636. }
  637. if (property_id == CSS::PropertyID::BorderWidth) {
  638. // FIXME: Remove string parsing once DeprecatedCSSParser is gone.
  639. if (value.is_string()) {
  640. auto parts = split_on_whitespace(value.to_string());
  641. if (parts.size() == 4) {
  642. auto top_border_width = parse_css_value(deprecated_context, parts[0]);
  643. auto right_border_width = parse_css_value(deprecated_context, parts[1]);
  644. auto bottom_border_width = parse_css_value(deprecated_context, parts[2]);
  645. auto left_border_width = parse_css_value(deprecated_context, parts[3]);
  646. if (top_border_width && right_border_width && bottom_border_width && left_border_width) {
  647. style.set_property(CSS::PropertyID::BorderTopWidth, *top_border_width);
  648. style.set_property(CSS::PropertyID::BorderRightWidth, *right_border_width);
  649. style.set_property(CSS::PropertyID::BorderBottomWidth, *bottom_border_width);
  650. style.set_property(CSS::PropertyID::BorderLeftWidth, *left_border_width);
  651. }
  652. } else if (parts.size() == 3) {
  653. auto top_border_width = parse_css_value(deprecated_context, parts[0]);
  654. auto horizontal_border_width = parse_css_value(deprecated_context, parts[1]);
  655. auto bottom_border_width = parse_css_value(deprecated_context, parts[2]);
  656. if (top_border_width && horizontal_border_width && bottom_border_width) {
  657. style.set_property(CSS::PropertyID::BorderTopWidth, *top_border_width);
  658. style.set_property(CSS::PropertyID::BorderRightWidth, *horizontal_border_width);
  659. style.set_property(CSS::PropertyID::BorderBottomWidth, *bottom_border_width);
  660. style.set_property(CSS::PropertyID::BorderLeftWidth, *horizontal_border_width);
  661. }
  662. } else if (parts.size() == 2) {
  663. auto vertical_border_width = parse_css_value(deprecated_context, parts[0]);
  664. auto horizontal_border_width = parse_css_value(deprecated_context, parts[1]);
  665. if (vertical_border_width && horizontal_border_width) {
  666. style.set_property(CSS::PropertyID::BorderTopWidth, *vertical_border_width);
  667. style.set_property(CSS::PropertyID::BorderRightWidth, *horizontal_border_width);
  668. style.set_property(CSS::PropertyID::BorderBottomWidth, *vertical_border_width);
  669. style.set_property(CSS::PropertyID::BorderLeftWidth, *horizontal_border_width);
  670. }
  671. } else {
  672. style.set_property(CSS::PropertyID::BorderTopWidth, value);
  673. style.set_property(CSS::PropertyID::BorderRightWidth, value);
  674. style.set_property(CSS::PropertyID::BorderBottomWidth, value);
  675. style.set_property(CSS::PropertyID::BorderLeftWidth, value);
  676. }
  677. return;
  678. }
  679. if (value.is_value_list()) {
  680. auto& parts = static_cast<CSS::ValueListStyleValue const&>(value).values();
  681. if (parts.size() == 4) {
  682. auto top_border_width = Parser::parse_css_value(context, property_id, parts[0]);
  683. auto right_border_width = Parser::parse_css_value(context, property_id, parts[1]);
  684. auto bottom_border_width = Parser::parse_css_value(context, property_id, parts[2]);
  685. auto left_border_width = Parser::parse_css_value(context, property_id, parts[3]);
  686. if (top_border_width && right_border_width && bottom_border_width && left_border_width) {
  687. style.set_property(CSS::PropertyID::BorderTopWidth, *top_border_width);
  688. style.set_property(CSS::PropertyID::BorderRightWidth, *right_border_width);
  689. style.set_property(CSS::PropertyID::BorderBottomWidth, *bottom_border_width);
  690. style.set_property(CSS::PropertyID::BorderLeftWidth, *left_border_width);
  691. }
  692. } else if (parts.size() == 3) {
  693. auto top_border_width = Parser::parse_css_value(context, property_id, parts[0]);
  694. auto horizontal_border_width = Parser::parse_css_value(context, property_id, parts[1]);
  695. auto bottom_border_width = Parser::parse_css_value(context, property_id, parts[2]);
  696. if (top_border_width && horizontal_border_width && bottom_border_width) {
  697. style.set_property(CSS::PropertyID::BorderTopWidth, *top_border_width);
  698. style.set_property(CSS::PropertyID::BorderRightWidth, *horizontal_border_width);
  699. style.set_property(CSS::PropertyID::BorderBottomWidth, *bottom_border_width);
  700. style.set_property(CSS::PropertyID::BorderLeftWidth, *horizontal_border_width);
  701. }
  702. } else if (parts.size() == 2) {
  703. auto vertical_border_width = Parser::parse_css_value(context, property_id, parts[0]);
  704. auto horizontal_border_width = Parser::parse_css_value(context, property_id, parts[1]);
  705. if (vertical_border_width && horizontal_border_width) {
  706. style.set_property(CSS::PropertyID::BorderTopWidth, *vertical_border_width);
  707. style.set_property(CSS::PropertyID::BorderRightWidth, *horizontal_border_width);
  708. style.set_property(CSS::PropertyID::BorderBottomWidth, *vertical_border_width);
  709. style.set_property(CSS::PropertyID::BorderLeftWidth, *horizontal_border_width);
  710. }
  711. } else {
  712. auto border_width = Parser::parse_css_value(context, property_id, parts[0]);
  713. if (border_width) {
  714. style.set_property(CSS::PropertyID::BorderTopWidth, *border_width);
  715. style.set_property(CSS::PropertyID::BorderRightWidth, *border_width);
  716. style.set_property(CSS::PropertyID::BorderBottomWidth, *border_width);
  717. style.set_property(CSS::PropertyID::BorderLeftWidth, *border_width);
  718. }
  719. }
  720. return;
  721. }
  722. style.set_property(CSS::PropertyID::BorderTopStyle, value);
  723. style.set_property(CSS::PropertyID::BorderRightStyle, value);
  724. style.set_property(CSS::PropertyID::BorderBottomStyle, value);
  725. style.set_property(CSS::PropertyID::BorderLeftStyle, value);
  726. return;
  727. }
  728. if (property_id == CSS::PropertyID::BorderColor) {
  729. // FIXME: Remove string parsing once DeprecatedCSSParser is gone.
  730. if (value.is_string()) {
  731. auto parts = split_on_whitespace(value.to_string());
  732. if (parts.size() == 4) {
  733. auto top = parse_css_value(deprecated_context, parts[0]);
  734. auto right = parse_css_value(deprecated_context, parts[1]);
  735. auto bottom = parse_css_value(deprecated_context, parts[2]);
  736. auto left = parse_css_value(deprecated_context, parts[3]);
  737. if (top && right && bottom && left) {
  738. style.set_property(CSS::PropertyID::BorderTopColor, *top);
  739. style.set_property(CSS::PropertyID::BorderRightColor, *right);
  740. style.set_property(CSS::PropertyID::BorderBottomColor, *bottom);
  741. style.set_property(CSS::PropertyID::BorderLeftColor, *left);
  742. }
  743. } else if (parts.size() == 3) {
  744. auto top = parse_css_value(deprecated_context, parts[0]);
  745. auto horizontal = parse_css_value(deprecated_context, parts[1]);
  746. auto bottom = parse_css_value(deprecated_context, parts[2]);
  747. if (top && horizontal && bottom) {
  748. style.set_property(CSS::PropertyID::BorderTopColor, *top);
  749. style.set_property(CSS::PropertyID::BorderRightColor, *horizontal);
  750. style.set_property(CSS::PropertyID::BorderBottomColor, *bottom);
  751. style.set_property(CSS::PropertyID::BorderLeftColor, *horizontal);
  752. }
  753. } else if (parts.size() == 2) {
  754. auto vertical = parse_css_value(deprecated_context, parts[0]);
  755. auto horizontal = parse_css_value(deprecated_context, parts[1]);
  756. if (vertical && horizontal) {
  757. style.set_property(CSS::PropertyID::BorderTopColor, *vertical);
  758. style.set_property(CSS::PropertyID::BorderRightColor, *horizontal);
  759. style.set_property(CSS::PropertyID::BorderBottomColor, *vertical);
  760. style.set_property(CSS::PropertyID::BorderLeftColor, *horizontal);
  761. }
  762. } else {
  763. style.set_property(CSS::PropertyID::BorderTopColor, value);
  764. style.set_property(CSS::PropertyID::BorderRightColor, value);
  765. style.set_property(CSS::PropertyID::BorderBottomColor, value);
  766. style.set_property(CSS::PropertyID::BorderLeftColor, value);
  767. }
  768. return;
  769. }
  770. if (value.is_value_list()) {
  771. auto& parts = static_cast<CSS::ValueListStyleValue const&>(value).values();
  772. if (parts.size() == 4) {
  773. auto top = Parser::parse_css_value(context, property_id, parts[0]);
  774. auto right = Parser::parse_css_value(context, property_id, parts[1]);
  775. auto bottom = Parser::parse_css_value(context, property_id, parts[2]);
  776. auto left = Parser::parse_css_value(context, property_id, parts[3]);
  777. if (top && right && bottom && left) {
  778. style.set_property(CSS::PropertyID::BorderTopColor, *top);
  779. style.set_property(CSS::PropertyID::BorderRightColor, *right);
  780. style.set_property(CSS::PropertyID::BorderBottomColor, *bottom);
  781. style.set_property(CSS::PropertyID::BorderLeftColor, *left);
  782. }
  783. } else if (parts.size() == 3) {
  784. auto top = Parser::parse_css_value(context, property_id, parts[0]);
  785. auto horizontal = Parser::parse_css_value(context, property_id, parts[1]);
  786. auto bottom = Parser::parse_css_value(context, property_id, parts[2]);
  787. if (top && horizontal && bottom) {
  788. style.set_property(CSS::PropertyID::BorderTopColor, *top);
  789. style.set_property(CSS::PropertyID::BorderRightColor, *horizontal);
  790. style.set_property(CSS::PropertyID::BorderBottomColor, *bottom);
  791. style.set_property(CSS::PropertyID::BorderLeftColor, *horizontal);
  792. }
  793. } else if (parts.size() == 2) {
  794. auto vertical = Parser::parse_css_value(context, property_id, parts[0]);
  795. auto horizontal = Parser::parse_css_value(context, property_id, parts[1]);
  796. if (vertical && horizontal) {
  797. style.set_property(CSS::PropertyID::BorderTopColor, *vertical);
  798. style.set_property(CSS::PropertyID::BorderRightColor, *horizontal);
  799. style.set_property(CSS::PropertyID::BorderBottomColor, *vertical);
  800. style.set_property(CSS::PropertyID::BorderLeftColor, *horizontal);
  801. }
  802. } else {
  803. auto color = Parser::parse_css_value(context, property_id, parts[0]);
  804. if (color) {
  805. style.set_property(CSS::PropertyID::BorderTopColor, *color);
  806. style.set_property(CSS::PropertyID::BorderRightColor, *color);
  807. style.set_property(CSS::PropertyID::BorderBottomColor, *color);
  808. style.set_property(CSS::PropertyID::BorderLeftColor, *color);
  809. }
  810. }
  811. return;
  812. }
  813. style.set_property(CSS::PropertyID::BorderTopColor, value);
  814. style.set_property(CSS::PropertyID::BorderRightColor, value);
  815. style.set_property(CSS::PropertyID::BorderBottomColor, value);
  816. style.set_property(CSS::PropertyID::BorderLeftColor, value);
  817. return;
  818. }
  819. if (property_id == CSS::PropertyID::Background) {
  820. if (value.is_identifier() && static_cast<IdentifierStyleValue const&>(value).id() == CSS::ValueID::None) {
  821. style.set_property(CSS::PropertyID::BackgroundColor, ColorStyleValue::create(Color::Transparent));
  822. return;
  823. }
  824. // FIXME: Remove string parsing once DeprecatedCSSParser is gone.
  825. if (value.is_string()) {
  826. auto parts = split_on_whitespace(value.to_string());
  827. NonnullRefPtrVector<StyleValue> values;
  828. for (auto& part : parts) {
  829. auto value = parse_css_value(deprecated_context, part);
  830. if (!value)
  831. return;
  832. values.append(value.release_nonnull());
  833. }
  834. // HACK: Disallow more than one color value in a 'background' shorthand
  835. size_t color_value_count = 0;
  836. for (auto& value : values)
  837. color_value_count += value.is_color();
  838. if (values[0].is_color() && color_value_count == 1)
  839. style.set_property(CSS::PropertyID::BackgroundColor, values[0]);
  840. for (auto it = values.begin(); it != values.end(); ++it) {
  841. auto& value = *it;
  842. if (is_background_repeat_property(value)) {
  843. if ((it + 1 != values.end()) && is_background_repeat_property(*(it + 1))) {
  844. ++it;
  845. set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundRepeatX, value, document, true);
  846. set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundRepeatY, *it, document, true);
  847. } else {
  848. set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundRepeat, value, document);
  849. }
  850. }
  851. if (!value.is_string())
  852. continue;
  853. set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundImage, value, document);
  854. }
  855. return;
  856. }
  857. // FIXME: Implement List parsing.
  858. return;
  859. }
  860. if (property_id == CSS::PropertyID::BackgroundImage) {
  861. // FIXME: Handle function StyleValues once we have them.
  862. if (!value.is_string())
  863. return;
  864. auto string = value.to_string();
  865. if (!string.starts_with("url("))
  866. return;
  867. if (!string.ends_with(')'))
  868. return;
  869. auto url = string.substring_view(4, string.length() - 5);
  870. if (url.length() >= 2 && url.starts_with('"') && url.ends_with('"'))
  871. url = url.substring_view(1, url.length() - 2);
  872. else if (url.length() >= 2 && url.starts_with('\'') && url.ends_with('\''))
  873. url = url.substring_view(1, url.length() - 2);
  874. auto background_image_value = ImageStyleValue::create(document.complete_url(url), document);
  875. style.set_property(CSS::PropertyID::BackgroundImage, move(background_image_value));
  876. return;
  877. }
  878. if (property_id == CSS::PropertyID::BackgroundRepeat) {
  879. // FIXME: Remove string parsing once DeprecatedCSSParser is gone.
  880. if (value.is_string()) {
  881. auto parts = split_on_whitespace(value.to_string());
  882. NonnullRefPtrVector<StyleValue> values;
  883. for (auto& part : parts) {
  884. auto value = parse_css_value(deprecated_context, part);
  885. if (!value || !is_background_repeat_property(*value))
  886. return;
  887. values.append(value.release_nonnull());
  888. }
  889. if (values.size() == 1) {
  890. auto value_id = values[0].to_identifier();
  891. if (value_id == CSS::ValueID::RepeatX || value_id == CSS::ValueID::RepeatY) {
  892. auto repeat_x = IdentifierStyleValue::create(value_id == CSS::ValueID::RepeatX ? CSS::ValueID::Repeat : CSS::ValueID::NoRepeat);
  893. auto repeat_y = IdentifierStyleValue::create(value_id == CSS::ValueID::RepeatX ? CSS::ValueID::NoRepeat : CSS::ValueID::Repeat);
  894. set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundRepeatX, repeat_x, document, true);
  895. set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundRepeatY, repeat_y, document, true);
  896. } else {
  897. set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundRepeatX, values[0], document, true);
  898. set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundRepeatY, values[0], document, true);
  899. }
  900. } else if (values.size() == 2) {
  901. set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundRepeatX, values[0], document, true);
  902. set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundRepeatY, values[1], document, true);
  903. }
  904. return;
  905. }
  906. // FIXME: Implement List parsing.
  907. return;
  908. }
  909. if (property_id == CSS::PropertyID::BackgroundRepeatX || property_id == CSS::PropertyID::BackgroundRepeatY) {
  910. auto value_id = value.to_identifier();
  911. if (value_id == CSS::ValueID::RepeatX || value_id == CSS::ValueID::RepeatY)
  912. return;
  913. style.set_property(property_id, value);
  914. return;
  915. }
  916. if (property_id == CSS::PropertyID::Margin) {
  917. if (value.is_length()) {
  918. style.set_property(CSS::PropertyID::MarginTop, value);
  919. style.set_property(CSS::PropertyID::MarginRight, value);
  920. style.set_property(CSS::PropertyID::MarginBottom, value);
  921. style.set_property(CSS::PropertyID::MarginLeft, value);
  922. return;
  923. }
  924. // FIXME: Remove string parsing once DeprecatedCSSParser is gone.
  925. if (value.is_string()) {
  926. auto parts = split_on_whitespace(value.to_string());
  927. if (parts.size() == 2) {
  928. auto vertical = parse_css_value(deprecated_context, parts[0]);
  929. auto horizontal = parse_css_value(deprecated_context, parts[1]);
  930. if (vertical && horizontal) {
  931. style.set_property(CSS::PropertyID::MarginTop, *vertical);
  932. style.set_property(CSS::PropertyID::MarginBottom, *vertical);
  933. style.set_property(CSS::PropertyID::MarginLeft, *horizontal);
  934. style.set_property(CSS::PropertyID::MarginRight, *horizontal);
  935. }
  936. return;
  937. } else if (parts.size() == 3) {
  938. auto top = parse_css_value(deprecated_context, parts[0]);
  939. auto horizontal = parse_css_value(deprecated_context, parts[1]);
  940. auto bottom = parse_css_value(deprecated_context, parts[2]);
  941. if (top && horizontal && bottom) {
  942. style.set_property(CSS::PropertyID::MarginTop, *top);
  943. style.set_property(CSS::PropertyID::MarginBottom, *bottom);
  944. style.set_property(CSS::PropertyID::MarginLeft, *horizontal);
  945. style.set_property(CSS::PropertyID::MarginRight, *horizontal);
  946. }
  947. return;
  948. } else if (parts.size() == 4) {
  949. auto top = parse_css_value(deprecated_context, parts[0]);
  950. auto right = parse_css_value(deprecated_context, parts[1]);
  951. auto bottom = parse_css_value(deprecated_context, parts[2]);
  952. auto left = parse_css_value(deprecated_context, parts[3]);
  953. if (top && right && bottom && left) {
  954. style.set_property(CSS::PropertyID::MarginTop, *top);
  955. style.set_property(CSS::PropertyID::MarginBottom, *bottom);
  956. style.set_property(CSS::PropertyID::MarginLeft, *left);
  957. style.set_property(CSS::PropertyID::MarginRight, *right);
  958. }
  959. return;
  960. }
  961. dbgln("Unsure what to do with CSS margin value '{}'", value.to_string());
  962. return;
  963. }
  964. if (value.is_value_list()) {
  965. auto parts = static_cast<CSS::ValueListStyleValue const&>(value).values();
  966. if (parts.size() == 2) {
  967. auto vertical = Parser::parse_css_value(context, property_id, parts[0]);
  968. auto horizontal = Parser::parse_css_value(context, property_id, parts[1]);
  969. if (vertical && horizontal) {
  970. style.set_property(CSS::PropertyID::MarginTop, *vertical);
  971. style.set_property(CSS::PropertyID::MarginBottom, *vertical);
  972. style.set_property(CSS::PropertyID::MarginLeft, *horizontal);
  973. style.set_property(CSS::PropertyID::MarginRight, *horizontal);
  974. }
  975. return;
  976. } else if (parts.size() == 3) {
  977. auto top = Parser::parse_css_value(context, property_id, parts[0]);
  978. auto horizontal = Parser::parse_css_value(context, property_id, parts[1]);
  979. auto bottom = Parser::parse_css_value(context, property_id, parts[2]);
  980. if (top && horizontal && bottom) {
  981. style.set_property(CSS::PropertyID::MarginTop, *top);
  982. style.set_property(CSS::PropertyID::MarginBottom, *bottom);
  983. style.set_property(CSS::PropertyID::MarginLeft, *horizontal);
  984. style.set_property(CSS::PropertyID::MarginRight, *horizontal);
  985. }
  986. return;
  987. } else if (parts.size() == 4) {
  988. auto top = Parser::parse_css_value(context, property_id, parts[0]);
  989. auto right = Parser::parse_css_value(context, property_id, parts[1]);
  990. auto bottom = Parser::parse_css_value(context, property_id, parts[2]);
  991. auto left = Parser::parse_css_value(context, property_id, parts[3]);
  992. if (top && right && bottom && left) {
  993. style.set_property(CSS::PropertyID::MarginTop, *top);
  994. style.set_property(CSS::PropertyID::MarginBottom, *bottom);
  995. style.set_property(CSS::PropertyID::MarginLeft, *left);
  996. style.set_property(CSS::PropertyID::MarginRight, *right);
  997. }
  998. return;
  999. }
  1000. dbgln("Unsure what to do with CSS margin value '{}'", value.to_string());
  1001. return;
  1002. }
  1003. return;
  1004. }
  1005. if (property_id == CSS::PropertyID::Padding) {
  1006. if (value.is_length()) {
  1007. style.set_property(CSS::PropertyID::PaddingTop, value);
  1008. style.set_property(CSS::PropertyID::PaddingRight, value);
  1009. style.set_property(CSS::PropertyID::PaddingBottom, value);
  1010. style.set_property(CSS::PropertyID::PaddingLeft, value);
  1011. return;
  1012. }
  1013. // FIXME: Remove string parsing once DeprecatedCSSParser is gone.
  1014. if (value.is_string()) {
  1015. auto parts = split_on_whitespace(value.to_string());
  1016. if (parts.size() == 2) {
  1017. auto vertical = parse_css_value(deprecated_context, parts[0]);
  1018. auto horizontal = parse_css_value(deprecated_context, parts[1]);
  1019. if (vertical && horizontal) {
  1020. style.set_property(CSS::PropertyID::PaddingTop, *vertical);
  1021. style.set_property(CSS::PropertyID::PaddingBottom, *vertical);
  1022. style.set_property(CSS::PropertyID::PaddingLeft, *horizontal);
  1023. style.set_property(CSS::PropertyID::PaddingRight, *horizontal);
  1024. }
  1025. return;
  1026. } else if (parts.size() == 3) {
  1027. auto top = parse_css_value(deprecated_context, parts[0]);
  1028. auto horizontal = parse_css_value(deprecated_context, parts[1]);
  1029. auto bottom = parse_css_value(deprecated_context, parts[2]);
  1030. if (top && bottom && horizontal) {
  1031. style.set_property(CSS::PropertyID::PaddingTop, *top);
  1032. style.set_property(CSS::PropertyID::PaddingBottom, *bottom);
  1033. style.set_property(CSS::PropertyID::PaddingLeft, *horizontal);
  1034. style.set_property(CSS::PropertyID::PaddingRight, *horizontal);
  1035. }
  1036. return;
  1037. } else if (parts.size() == 4) {
  1038. auto top = parse_css_value(deprecated_context, parts[0]);
  1039. auto right = parse_css_value(deprecated_context, parts[1]);
  1040. auto bottom = parse_css_value(deprecated_context, parts[2]);
  1041. auto left = parse_css_value(deprecated_context, parts[3]);
  1042. if (top && bottom && left && right) {
  1043. style.set_property(CSS::PropertyID::PaddingTop, *top);
  1044. style.set_property(CSS::PropertyID::PaddingBottom, *bottom);
  1045. style.set_property(CSS::PropertyID::PaddingLeft, *left);
  1046. style.set_property(CSS::PropertyID::PaddingRight, *right);
  1047. }
  1048. return;
  1049. }
  1050. dbgln("Unsure what to do with CSS padding value '{}'", value.to_string());
  1051. return;
  1052. }
  1053. if (value.is_value_list()) {
  1054. auto parts = static_cast<CSS::ValueListStyleValue const&>(value).values();
  1055. if (parts.size() == 2) {
  1056. auto vertical = Parser::parse_css_value(context, property_id, parts[0]);
  1057. auto horizontal = Parser::parse_css_value(context, property_id, parts[1]);
  1058. if (vertical && horizontal) {
  1059. style.set_property(CSS::PropertyID::PaddingTop, *vertical);
  1060. style.set_property(CSS::PropertyID::PaddingBottom, *vertical);
  1061. style.set_property(CSS::PropertyID::PaddingLeft, *horizontal);
  1062. style.set_property(CSS::PropertyID::PaddingRight, *horizontal);
  1063. }
  1064. return;
  1065. } else if (parts.size() == 3) {
  1066. auto top = Parser::parse_css_value(context, property_id, parts[0]);
  1067. auto horizontal = Parser::parse_css_value(context, property_id, parts[1]);
  1068. auto bottom = Parser::parse_css_value(context, property_id, parts[2]);
  1069. if (top && bottom && horizontal) {
  1070. style.set_property(CSS::PropertyID::PaddingTop, *top);
  1071. style.set_property(CSS::PropertyID::PaddingBottom, *bottom);
  1072. style.set_property(CSS::PropertyID::PaddingLeft, *horizontal);
  1073. style.set_property(CSS::PropertyID::PaddingRight, *horizontal);
  1074. }
  1075. return;
  1076. } else if (parts.size() == 4) {
  1077. auto top = Parser::parse_css_value(context, property_id, parts[0]);
  1078. auto right = Parser::parse_css_value(context, property_id, parts[1]);
  1079. auto bottom = Parser::parse_css_value(context, property_id, parts[2]);
  1080. auto left = Parser::parse_css_value(context, property_id, parts[3]);
  1081. if (top && bottom && left && right) {
  1082. style.set_property(CSS::PropertyID::PaddingTop, *top);
  1083. style.set_property(CSS::PropertyID::PaddingBottom, *bottom);
  1084. style.set_property(CSS::PropertyID::PaddingLeft, *left);
  1085. style.set_property(CSS::PropertyID::PaddingRight, *right);
  1086. }
  1087. return;
  1088. }
  1089. dbgln("Unsure what to do with CSS padding value '{}'", value.to_string());
  1090. return;
  1091. }
  1092. return;
  1093. }
  1094. if (property_id == CSS::PropertyID::ListStyle) {
  1095. // FIXME: Remove string parsing once DeprecatedCSSParser is gone.
  1096. if (value.is_string()) {
  1097. auto parts = split_on_whitespace(value.to_string());
  1098. if (!parts.is_empty()) {
  1099. auto value = parse_css_value(deprecated_context, parts[0]);
  1100. if (!value)
  1101. return;
  1102. style.set_property(CSS::PropertyID::ListStyleType, value.release_nonnull());
  1103. }
  1104. return;
  1105. }
  1106. // FIXME: Handle all three parts of ListStyle. (list-style-positon, list-style-image, list-style-type)
  1107. if (value.is_value_list()) {
  1108. auto parts = static_cast<CSS::ValueListStyleValue const&>(value).values();
  1109. if (!parts.is_empty()) {
  1110. auto list_style = Parser::parse_css_value(context, property_id, parts[0]);
  1111. if (list_style)
  1112. style.set_property(CSS::PropertyID::ListStyleType, *list_style);
  1113. }
  1114. return;
  1115. }
  1116. return;
  1117. }
  1118. // FIXME: parse other values as well
  1119. if (property_id == CSS::PropertyID::Font) {
  1120. // FIXME: Remove string parsing once DeprecatedCSSParser is gone.
  1121. if (value.is_string()) {
  1122. auto parts = split_on_whitespace(value.to_string());
  1123. if (parts.size() < 2)
  1124. return;
  1125. auto size_parts = parts[0].split_view('/');
  1126. if (size_parts.size() == 2) {
  1127. auto size = parse_css_value(deprecated_context, size_parts[0]);
  1128. auto line_height = parse_css_value(deprecated_context, size_parts[1]);
  1129. if (!size || !line_height)
  1130. return;
  1131. style.set_property(CSS::PropertyID::FontSize, size.release_nonnull());
  1132. style.set_property(CSS::PropertyID::LineHeight, line_height.release_nonnull());
  1133. } else if (size_parts.size() == 1) {
  1134. auto size = parse_css_value(deprecated_context, parts[0]);
  1135. if (!size)
  1136. return;
  1137. style.set_property(CSS::PropertyID::FontSize, size.release_nonnull());
  1138. }
  1139. auto family = parse_css_value(deprecated_context, parts[1]);
  1140. style.set_property(CSS::PropertyID::FontFamily, family.release_nonnull());
  1141. return;
  1142. }
  1143. // FIXME: Handle this as a ValueListStyleValue.
  1144. return;
  1145. }
  1146. if (property_id == CSS::PropertyID::Flex) {
  1147. if (value.is_length() || (value.is_identifier() && value.to_identifier() == CSS::ValueID::Content)) {
  1148. style.set_property(CSS::PropertyID::FlexBasis, value);
  1149. return;
  1150. }
  1151. // FIXME: Remove string parsing once DeprecatedCSSParser is gone.
  1152. if (value.is_string()) {
  1153. auto parts = split_on_whitespace(value.to_string());
  1154. if (parts.size() == 1) {
  1155. auto flex_grow = parse_css_value(deprecated_context, parts[0]);
  1156. style.set_property(CSS::PropertyID::FlexGrow, *flex_grow);
  1157. return;
  1158. }
  1159. if (parts.size() == 2) {
  1160. auto flex_grow = parse_css_value(deprecated_context, parts[0]);
  1161. style.set_property(CSS::PropertyID::FlexGrow, *flex_grow);
  1162. auto second_value = parse_css_value(deprecated_context, parts[1]);
  1163. if (second_value->is_length() || (second_value->is_identifier() && second_value->to_identifier() == CSS::ValueID::Content)) {
  1164. style.set_property(CSS::PropertyID::FlexBasis, *second_value);
  1165. } else {
  1166. auto flex_shrink = parse_css_value(deprecated_context, parts[1]);
  1167. style.set_property(CSS::PropertyID::FlexShrink, *flex_shrink);
  1168. }
  1169. return;
  1170. }
  1171. if (parts.size() == 3) {
  1172. auto flex_grow = parse_css_value(deprecated_context, parts[0]);
  1173. style.set_property(CSS::PropertyID::FlexGrow, *flex_grow);
  1174. auto flex_shrink = parse_css_value(deprecated_context, parts[1]);
  1175. style.set_property(CSS::PropertyID::FlexShrink, *flex_shrink);
  1176. auto third_value = parse_css_value(deprecated_context, parts[2]);
  1177. if (third_value->is_length() || (third_value->is_identifier() && third_value->to_identifier() == CSS::ValueID::Content))
  1178. style.set_property(CSS::PropertyID::FlexBasis, *third_value);
  1179. return;
  1180. }
  1181. return;
  1182. }
  1183. if (value.is_value_list()) {
  1184. auto parts = static_cast<CSS::ValueListStyleValue const&>(value).values();
  1185. if (parts.size() == 1) {
  1186. auto value = Parser::parse_css_value(context, property_id, parts[0]);
  1187. if (!value)
  1188. return;
  1189. if (is_flex_basis(*value)) {
  1190. style.set_property(CSS::PropertyID::FlexBasis, *value);
  1191. } else if (is_flex_grow_or_shrink(*value)) {
  1192. style.set_property(CSS::PropertyID::FlexGrow, *value);
  1193. }
  1194. return;
  1195. }
  1196. if (parts.size() == 2) {
  1197. auto flex_grow = Parser::parse_css_value(context, property_id, parts[0]);
  1198. auto second_value = Parser::parse_css_value(context, property_id, parts[1]);
  1199. if (!flex_grow || !second_value)
  1200. return;
  1201. style.set_property(CSS::PropertyID::FlexGrow, *flex_grow);
  1202. if (is_flex_basis(*second_value)) {
  1203. style.set_property(CSS::PropertyID::FlexBasis, *second_value);
  1204. } else if (is_flex_grow_or_shrink(*second_value)) {
  1205. style.set_property(CSS::PropertyID::FlexShrink, *second_value);
  1206. }
  1207. return;
  1208. }
  1209. if (parts.size() == 3) {
  1210. auto flex_grow = Parser::parse_css_value(context, property_id, parts[0]);
  1211. auto flex_shrink = Parser::parse_css_value(context, property_id, parts[1]);
  1212. auto flex_basis = Parser::parse_css_value(context, property_id, parts[2]);
  1213. if (!flex_grow || !flex_shrink || !flex_basis)
  1214. return;
  1215. style.set_property(CSS::PropertyID::FlexGrow, *flex_grow);
  1216. style.set_property(CSS::PropertyID::FlexShrink, *flex_shrink);
  1217. if (is_flex_basis(*flex_basis))
  1218. style.set_property(CSS::PropertyID::FlexBasis, *flex_basis);
  1219. return;
  1220. }
  1221. return;
  1222. }
  1223. dbgln("Unsure what to do with CSS flex value '{}'", value.to_string());
  1224. return;
  1225. }
  1226. if (property_id == CSS::PropertyID::FlexFlow) {
  1227. // FIXME: Remove string parsing once DeprecatedCSSParser is gone.
  1228. if (value.is_string()) {
  1229. auto parts = split_on_whitespace(value.to_string());
  1230. if (parts.is_empty())
  1231. return;
  1232. auto direction = parse_css_value(deprecated_context, parts[0]);
  1233. style.set_property(CSS::PropertyID::FlexDirection, direction.release_nonnull());
  1234. if (parts.size() > 1) {
  1235. auto wrap = parse_css_value(deprecated_context, parts[1]);
  1236. style.set_property(CSS::PropertyID::FlexWrap, wrap.release_nonnull());
  1237. }
  1238. return;
  1239. }
  1240. if (value.is_value_list()) {
  1241. auto parts = static_cast<CSS::ValueListStyleValue const&>(value).values();
  1242. if (parts.is_empty() || parts.size() > 2)
  1243. return;
  1244. RefPtr<StyleValue> flex_direction_value;
  1245. RefPtr<StyleValue> flex_wrap_value;
  1246. for (auto& part : parts) {
  1247. auto value = Parser::parse_css_value(context, property_id, part);
  1248. if (!value)
  1249. return;
  1250. if (is_flex_direction(*value)) {
  1251. if (flex_direction_value)
  1252. return;
  1253. flex_direction_value = move(value);
  1254. continue;
  1255. }
  1256. if (is_flex_wrap(*value)) {
  1257. if (flex_wrap_value)
  1258. return;
  1259. flex_wrap_value = move(value);
  1260. continue;
  1261. }
  1262. }
  1263. if (flex_direction_value)
  1264. style.set_property(CSS::PropertyID::FlexDirection, *flex_direction_value);
  1265. if (flex_wrap_value)
  1266. style.set_property(CSS::PropertyID::FlexWrap, *flex_wrap_value);
  1267. return;
  1268. }
  1269. return;
  1270. }
  1271. if (value.is_value_list()) {
  1272. dbgln("Values list for CSS property '{}' went unhandled. List: '{}'", string_from_property_id(property_id), value.to_string());
  1273. return;
  1274. }
  1275. style.set_property(property_id, value);
  1276. }
  1277. StyleResolver::CustomPropertyResolutionTuple StyleResolver::resolve_custom_property_with_specificity(DOM::Element& element, String const& custom_property_name) const
  1278. {
  1279. if (auto maybe_property = element.resolve_custom_property(custom_property_name); maybe_property.has_value())
  1280. return maybe_property.value();
  1281. auto parent_element = element.parent_element();
  1282. CustomPropertyResolutionTuple parent_resolved {};
  1283. if (parent_element)
  1284. parent_resolved = resolve_custom_property_with_specificity(*parent_element, custom_property_name);
  1285. auto matching_rules = collect_matching_rules(element);
  1286. sort_matching_rules(matching_rules);
  1287. for (int i = matching_rules.size() - 1; i >= 0; --i) {
  1288. auto& match = matching_rules[i];
  1289. if (match.specificity < parent_resolved.specificity)
  1290. continue;
  1291. auto custom_property_style = match.rule->declaration().custom_property(custom_property_name);
  1292. if (custom_property_style.has_value()) {
  1293. element.add_custom_property(custom_property_name, { custom_property_style.value(), match.specificity });
  1294. return { custom_property_style.value(), match.specificity };
  1295. }
  1296. }
  1297. return parent_resolved;
  1298. }
  1299. Optional<StyleProperty> StyleResolver::resolve_custom_property(DOM::Element& element, String const& custom_property_name) const
  1300. {
  1301. auto resolved_with_specificity = resolve_custom_property_with_specificity(element, custom_property_name);
  1302. return resolved_with_specificity.style;
  1303. }
  1304. NonnullRefPtr<StyleProperties> StyleResolver::resolve_style(DOM::Element& element) const
  1305. {
  1306. auto style = StyleProperties::create();
  1307. if (auto* parent_style = element.parent_element() ? element.parent_element()->specified_css_values() : nullptr) {
  1308. parent_style->for_each_property([&](auto property_id, auto& value) {
  1309. if (is_inherited_property(property_id))
  1310. set_property_expanding_shorthands(style, property_id, value, m_document);
  1311. });
  1312. }
  1313. element.apply_presentational_hints(*style);
  1314. auto matching_rules = collect_matching_rules(element);
  1315. sort_matching_rules(matching_rules);
  1316. for (auto& match : matching_rules) {
  1317. for (auto& property : match.rule->declaration().properties()) {
  1318. auto property_value = property.value;
  1319. if (property.value->is_custom_property()) {
  1320. auto prop = reinterpret_cast<CSS::CustomStyleValue const*>(property.value.ptr());
  1321. auto custom_prop_name = prop->custom_property_name();
  1322. auto resolved = resolve_custom_property(element, custom_prop_name);
  1323. if (resolved.has_value()) {
  1324. property_value = resolved.value().value;
  1325. }
  1326. }
  1327. set_property_expanding_shorthands(style, property.property_id, property_value, m_document);
  1328. }
  1329. }
  1330. if (auto* inline_style = element.inline_style()) {
  1331. for (auto& property : inline_style->properties()) {
  1332. set_property_expanding_shorthands(style, property.property_id, property.value, m_document);
  1333. }
  1334. }
  1335. return style;
  1336. }
  1337. }