/src/serenity/Userland/Libraries/LibWeb/HTML/WebViewHints.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include <LibIPC/Decoder.h> |
8 | | #include <LibIPC/Encoder.h> |
9 | | #include <LibWeb/HTML/Numbers.h> |
10 | | #include <LibWeb/HTML/WebViewHints.h> |
11 | | #include <LibWeb/Page/Page.h> |
12 | | #include <LibWeb/PixelUnits.h> |
13 | | |
14 | | namespace Web::HTML { |
15 | | |
16 | | static void set_up_browsing_context_features(WebViewHints& target, TokenizedFeature::Map const& tokenized_features, Page const& page); |
17 | | |
18 | | WebViewHints WebViewHints::from_tokenised_features(TokenizedFeature::Map const& tokenized_features, Page const& page) |
19 | 0 | { |
20 | 0 | WebViewHints hints; |
21 | 0 | hints.popup = check_if_a_popup_window_is_requested(tokenized_features) == TokenizedFeature::Popup::Yes; |
22 | 0 | set_up_browsing_context_features(hints, tokenized_features, page); |
23 | 0 | return hints; |
24 | 0 | } |
25 | | |
26 | | // https://drafts.csswg.org/cssom-view/#set-up-browsing-context-features |
27 | | static void set_up_browsing_context_features(WebViewHints& target, TokenizedFeature::Map const& tokenized_features, Page const& page) |
28 | 0 | { |
29 | | // 1. Let x be null. |
30 | 0 | Optional<CSSPixels> x; |
31 | | |
32 | | // 2. Let y be null. |
33 | 0 | Optional<CSSPixels> y; |
34 | | |
35 | | // 3. Let width be null. |
36 | 0 | Optional<CSSPixels> width; |
37 | | |
38 | | // 4. Let height be null. |
39 | 0 | Optional<CSSPixels> height; |
40 | |
|
41 | 0 | auto screen_rect = page.web_exposed_screen_area(); |
42 | | |
43 | | // 5. If tokenizedFeatures["left"] exists: |
44 | 0 | if (auto left = tokenized_features.get("left"sv); left.has_value()) { |
45 | | // 1. Set x to the result of invoking the rules for parsing integers on tokenizedFeatures["left"]. |
46 | | // 2. If x is an error, set x to 0. |
47 | 0 | x = parse_integer(*left).value_or(0); |
48 | | |
49 | | // 3. Optionally, clamp x in a user-agent-defined manner so that the window does not move outside the Web-exposed available screen area. |
50 | 0 | x = min(*x, screen_rect.x()); |
51 | | |
52 | | // 4. Optionally, move target’s window such that the window’s left edge is at the horizontal coordinate x relative |
53 | | // to the left edge of the Web-exposed screen area, measured in CSS pixels of target. The positive axis is rightward. |
54 | | // Note: Handled in the UI process when creating the traversable navigable. |
55 | 0 | } |
56 | | |
57 | | // 6. If tokenizedFeatures["top"] exists: |
58 | 0 | if (auto top = tokenized_features.get("top"sv); top.has_value()) { |
59 | | // 1. Set y to the result of invoking the rules for parsing integers on tokenizedFeatures["top"]. |
60 | | // 2. If y is an error, set y to 0. |
61 | 0 | y = parse_integer(*top).value_or(0); |
62 | | |
63 | | // 3. Optionally, clamp y in a user-agent-defined manner so that the window does not move outside the Web-exposed available screen area. |
64 | 0 | y = min(*y, screen_rect.y()); |
65 | | |
66 | | // 4. Optionally, move target’s window such that the window’s top edge is at the vertical coordinate y relative |
67 | | // to the top edge of the Web-exposed screen area, measured in CSS pixels of target. The positive axis is downward. |
68 | | // Note: Handled in the UI process when creating the traversable navigable. |
69 | 0 | } |
70 | | |
71 | | // 7. If tokenizedFeatures["width"] exists: |
72 | 0 | if (auto width_token = tokenized_features.get("width"sv); width_token.has_value()) { |
73 | | // 1. Set width to the result of invoking the rules for parsing integers on tokenizedFeatures["width"]. |
74 | | // 2. If width is an error, set width to 0. |
75 | 0 | width = parse_integer(*width_token).value_or(0); |
76 | | |
77 | | // 3. If width is not 0: |
78 | 0 | if (width != 0) { |
79 | | // 1. Optionally, clamp width in a user-agent-defined manner so that the window does not get too small or bigger than the Web-exposed available screen area. |
80 | 0 | width = clamp(*width, 100, screen_rect.width()); |
81 | | |
82 | | // 2. Optionally, size target’s window by moving its right edge such that the distance between the left and right edges of the viewport are width CSS pixels of target. |
83 | | // 3. Optionally, move target’s window in a user-agent-defined manner so that it does not grow outside the Web-exposed available screen area. |
84 | | // Note: Handled in the UI process when creating the traversable navigable. |
85 | 0 | } |
86 | 0 | } |
87 | | |
88 | | // 8. If tokenizedFeatures["height"] exists: |
89 | 0 | if (auto height_token = tokenized_features.get("height"sv); height_token.has_value()) { |
90 | | // 1. Set height to the result of invoking the rules for parsing integers on tokenizedFeatures["height"]. |
91 | | // 2. If height is an error, set height to 0. |
92 | 0 | height = parse_integer(*height_token).value_or(0); |
93 | | |
94 | | // 3. If height is not 0: |
95 | 0 | if (height != 0) { |
96 | | // 1. Optionally, clamp height in a user-agent-defined manner so that the window does not get too small or bigger than the Web-exposed available screen area. |
97 | 0 | height = clamp(*height, 100, screen_rect.height()); |
98 | | |
99 | | // 2. Optionally, size target’s window by moving its bottom edge such that the distance between the top and bottom edges of the viewport are height CSS pixels of target. |
100 | | // 3. Optionally, move target’s window in a user-agent-defined manner so that it does not grow outside the Web-exposed available screen area. |
101 | | // Note: Handled in the UI process when creating the traversable navigable. |
102 | 0 | } |
103 | 0 | } |
104 | |
|
105 | 0 | auto scale = page.client().device_pixels_per_css_pixel(); |
106 | |
|
107 | 0 | if (x.has_value()) { |
108 | | // Make sure we don't fly off the screen to the right |
109 | 0 | if (x.value() + width.value_or(0) > screen_rect.width()) |
110 | 0 | x = screen_rect.width() - width.value_or(0); |
111 | 0 | target.screen_x = x.value() / scale; |
112 | 0 | } |
113 | |
|
114 | 0 | if (y.has_value()) { |
115 | | // Make sure we don't fly off the screen to the bottom |
116 | 0 | if (y.value() + height.value_or(0) > screen_rect.height()) |
117 | 0 | y = screen_rect.height() - height.value_or(0); |
118 | 0 | target.screen_y = y.value() / scale; |
119 | 0 | } |
120 | |
|
121 | 0 | if (width.has_value()) { |
122 | 0 | target.width = width.value() / scale; |
123 | 0 | } |
124 | |
|
125 | 0 | if (height.has_value()) { |
126 | 0 | target.height = height.value() / scale; |
127 | 0 | } |
128 | 0 | } |
129 | | |
130 | | } |
131 | | |
132 | | namespace IPC { |
133 | | |
134 | | template<> |
135 | | ErrorOr<void> encode(Encoder& encoder, ::Web::HTML::WebViewHints const& data_holder) |
136 | 0 | { |
137 | 0 | TRY(encoder.encode(data_holder.popup)); |
138 | 0 | TRY(encoder.encode(data_holder.width)); |
139 | 0 | TRY(encoder.encode(data_holder.height)); |
140 | 0 | TRY(encoder.encode(data_holder.screen_x)); |
141 | 0 | TRY(encoder.encode(data_holder.screen_y)); |
142 | | |
143 | 0 | return {}; |
144 | 0 | } |
145 | | |
146 | | template<> |
147 | | ErrorOr<::Web::HTML::WebViewHints> decode(Decoder& decoder) |
148 | 0 | { |
149 | 0 | auto popup = TRY(decoder.decode<bool>()); |
150 | 0 | auto width = TRY(decoder.decode<Optional<Web::DevicePixels>>()); |
151 | 0 | auto height = TRY(decoder.decode<Optional<Web::DevicePixels>>()); |
152 | 0 | auto screen_x = TRY(decoder.decode<Optional<Web::DevicePixels>>()); |
153 | 0 | auto screen_y = TRY(decoder.decode<Optional<Web::DevicePixels>>()); |
154 | | |
155 | 0 | return ::Web::HTML::WebViewHints { |
156 | 0 | .popup = popup, |
157 | 0 | .width = width, |
158 | 0 | .height = height, |
159 | 0 | .screen_x = screen_x, |
160 | 0 | .screen_y = screen_y, |
161 | 0 | }; |
162 | 0 | } |
163 | | |
164 | | } |