1use std::cell::{Cell, Ref, RefCell, RefMut};
6use std::hash::Hash;
7use std::rc::{Rc, Weak};
8
9use accesskit::{
10 Affine as AccesskitAffine, Node as AccesskitNode, NodeId, Rect as AccesskitRect, Role, Tree,
11 TreeId, TreeUpdate, Uuid as AccesskitUuid,
12};
13use dpi::PhysicalSize;
14use embedder_traits::{
15 ContextMenuAction, ContextMenuItem, Cursor, EmbedderControlId, EmbedderControlRequest, Image,
16 InputEvent, InputEventAndId, InputEventId, JSValue, JavaScriptEvaluationError, LoadStatus,
17 MediaSessionActionType, NewWebViewDetails, ScreenGeometry, ScreenshotCaptureError, Scroll,
18 Theme, TraversalId, UrlRequest, ViewportDetails, WebViewPoint, WebViewRect,
19};
20use euclid::{Scale, Size2D};
21use image::RgbaImage;
22use log::debug;
23use paint_api::WebViewTrait;
24use paint_api::rendering_context::RenderingContext;
25use servo_base::Epoch;
26use servo_base::generic_channel::GenericSender;
27use servo_base::id::WebViewId;
28use servo_config::pref;
29use servo_constellation_traits::{
30 EmbedderToConstellationMessage, HistoryTraversalSource, SessionHistoryTraversalRequest,
31 TraversalDirection,
32};
33use servo_geometry::DeviceIndependentPixel;
34use servo_url::ServoUrl;
35use style_traits::CSSPixel;
36use url::Url;
37use webrender_api::units::{DeviceIntRect, DevicePixel, DevicePoint, DeviceSize};
38
39use crate::clipboard_delegate::{ClipboardDelegate, DefaultClipboardDelegate};
40#[cfg(feature = "gamepad")]
41use crate::gamepad_delegate::{DefaultGamepadDelegate, GamepadDelegate};
42use crate::geolocation_delegate::{DefaultGeolocationDelegate, GeolocationDelegate};
43use crate::responders::AutomaticResponder;
44use crate::servo::PendingHandledInputEvent;
45use crate::webview_delegate::{CreateNewWebViewRequest, DefaultWebViewDelegate, WebViewDelegate};
46use crate::{
47 ColorPicker, ContextMenu, EmbedderControl, InputMethodControl, SelectElement, Servo,
48 UserContentManager, WebRenderDebugOption,
49};
50
51pub(crate) const MINIMUM_WEBVIEW_SIZE: Size2D<i32, DevicePixel> = Size2D::new(1, 1);
52
53#[derive(Clone)]
85pub struct WebView(Rc<RefCell<WebViewInner>>);
86
87impl PartialEq for WebView {
88 fn eq(&self, other: &Self) -> bool {
89 self.inner().id == other.inner().id
90 }
91}
92
93impl Hash for WebView {
94 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
95 self.inner().id.hash(state);
96 }
97}
98
99pub(crate) struct WebViewInner {
100 pub(crate) id: WebViewId,
101 pub(crate) servo: Servo,
102 pub(crate) delegate: Rc<dyn WebViewDelegate>,
103 pub(crate) clipboard_delegate: Rc<dyn ClipboardDelegate>,
104 #[cfg(feature = "gamepad")]
105 pub(crate) gamepad_delegate: Rc<dyn GamepadDelegate>,
106 pub(crate) geolocation_delegate: Rc<dyn GeolocationDelegate>,
107
108 pub(crate) accesskit_tree_id: Option<TreeId>,
113 pub(crate) grafted_accesskit_tree_id: Option<TreeId>,
116 grafted_accesskit_tree_epoch: Option<Epoch>,
119 accessibility_viewport_changed: Cell<bool>,
127
128 rendering_context: Rc<dyn RenderingContext>,
129 user_content_manager: Option<Rc<UserContentManager>>,
130 hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
131 load_status: LoadStatus,
132 status_text: Option<String>,
133 page_title: Option<String>,
134 favicon: Option<Image>,
135 focused: bool,
136 animating: bool,
137 cursor: Cursor,
138
139 back_forward_list: Vec<Url>,
141
142 back_forward_list_index: usize,
144}
145
146impl Drop for WebViewInner {
147 fn drop(&mut self) {
148 self.servo
149 .constellation_proxy()
150 .send(EmbedderToConstellationMessage::CloseWebView(self.id));
151 self.servo.paint_mut().remove_webview(self.id);
152 }
153}
154
155impl WebView {
156 pub(crate) fn new(mut builder: WebViewBuilder) -> Self {
157 let servo = builder.servo;
158 let painter_id = servo
159 .paint_mut()
160 .register_rendering_context(builder.rendering_context.clone());
161
162 let id = WebViewId::new(painter_id);
163 let webview = Self(Rc::new(RefCell::new(WebViewInner {
164 id,
165 servo: servo.clone(),
166 rendering_context: builder.rendering_context,
167 delegate: builder.delegate,
168 clipboard_delegate: builder
169 .clipboard_delegate
170 .unwrap_or_else(|| Rc::new(DefaultClipboardDelegate)),
171 #[cfg(feature = "gamepad")]
172 gamepad_delegate: builder
173 .gamepad_delegate
174 .unwrap_or_else(|| Rc::new(DefaultGamepadDelegate)),
175 geolocation_delegate: builder
176 .geolocation_delegate
177 .unwrap_or_else(|| Rc::new(DefaultGeolocationDelegate)),
178 accesskit_tree_id: None,
179 grafted_accesskit_tree_id: None,
180 grafted_accesskit_tree_epoch: None,
181 accessibility_viewport_changed: Cell::new(false),
182 hidpi_scale_factor: builder.hidpi_scale_factor,
183 load_status: LoadStatus::Started,
184 status_text: None,
185 page_title: None,
186 favicon: None,
187 focused: true,
188 animating: false,
189 cursor: Cursor::Pointer,
190 back_forward_list: Default::default(),
191 back_forward_list_index: 0,
192 user_content_manager: builder.user_content_manager.clone(),
193 })));
194
195 let viewport_details = webview.viewport_details();
196 servo.paint().add_webview(
197 Box::new(ServoRendererWebView {
198 weak_handle: webview.weak_handle(),
199 id,
200 }),
201 viewport_details,
202 );
203
204 servo
205 .webviews_mut()
206 .insert(webview.id(), webview.weak_handle());
207
208 let user_content_manager_id = builder
209 .user_content_manager
210 .as_ref()
211 .map(|user_content_manager| user_content_manager.id());
212
213 let new_webview_details = NewWebViewDetails {
214 webview_id: webview.id(),
215 viewport_details,
216 user_content_manager_id,
217 };
218
219 match builder.create_new_webview_responder.as_mut() {
225 Some(responder) => {
226 let _ = responder.send(Some(new_webview_details));
227 },
228 None => {
229 let url = builder.url.unwrap_or(
230 Url::parse("about:blank")
231 .expect("Should always be able to parse 'about:blank'."),
232 );
233
234 servo
235 .constellation_proxy()
236 .send(EmbedderToConstellationMessage::NewWebView(
237 url.into(),
238 new_webview_details,
239 ));
240 },
241 }
242
243 webview
244 }
245
246 fn inner(&self) -> Ref<'_, WebViewInner> {
247 self.0.borrow()
248 }
249
250 fn inner_mut(&self) -> RefMut<'_, WebViewInner> {
251 self.0.borrow_mut()
252 }
253
254 fn servo(&self) -> Servo {
255 self.0.borrow().servo.clone()
256 }
257
258 pub(crate) fn request_create_new(
259 &self,
260 response_sender: GenericSender<Option<NewWebViewDetails>>,
261 ) {
262 let request = CreateNewWebViewRequest {
263 servo: self.servo(),
264 responder: AutomaticResponder::new(response_sender, None),
265 };
266 self.delegate().request_create_new(self.clone(), request);
267 }
268
269 pub(crate) fn viewport_details(&self) -> ViewportDetails {
270 let inner = self.inner();
273 let viewport_size = inner.rendering_context.size2d().to_f32();
274 let scaled_viewport_size = viewport_size / inner.hidpi_scale_factor;
275 let device_size = self
276 .delegate()
277 .screen_geometry(self.clone())
278 .map(|geometry| geometry.size.to_f32())
279 .unwrap_or_else(|| viewport_size);
280 ViewportDetails {
281 size: scaled_viewport_size / Scale::new(1.0),
282 hidpi_scale_factor: Scale::new(inner.hidpi_scale_factor.0),
283 device_size,
284 }
285 }
286
287 pub(crate) fn from_weak_handle(inner: &Weak<RefCell<WebViewInner>>) -> Option<Self> {
288 inner.upgrade().map(WebView)
289 }
290
291 pub(crate) fn weak_handle(&self) -> Weak<RefCell<WebViewInner>> {
292 Rc::downgrade(&self.0)
293 }
294
295 pub fn delegate(&self) -> Rc<dyn WebViewDelegate> {
297 self.inner().delegate.clone()
298 }
299
300 pub fn clipboard_delegate(&self) -> Rc<dyn ClipboardDelegate> {
302 self.inner().clipboard_delegate.clone()
303 }
304
305 #[cfg(feature = "gamepad")]
307 pub fn gamepad_delegate(&self) -> Rc<dyn GamepadDelegate> {
308 self.inner().gamepad_delegate.clone()
309 }
310
311 pub fn geolocation_delegate(&self) -> Rc<dyn GeolocationDelegate> {
313 self.inner().geolocation_delegate.clone()
314 }
315
316 pub fn id(&self) -> WebViewId {
318 self.inner().id
319 }
320
321 pub fn rendering_context(&self) -> Rc<dyn RenderingContext> {
323 self.inner().rendering_context.clone()
324 }
325
326 pub fn load_status(&self) -> LoadStatus {
331 self.inner().load_status
332 }
333
334 pub(crate) fn set_load_status(self, new_value: LoadStatus) {
335 if self.inner().load_status == new_value {
336 return;
337 }
338 self.inner_mut().load_status = new_value;
339 self.delegate().notify_load_status_changed(self, new_value);
340 }
341
342 pub fn url(&self) -> Option<Url> {
345 let inner = self.inner();
346 inner
347 .back_forward_list
348 .get(inner.back_forward_list_index)
349 .cloned()
350 }
351
352 pub fn status_text(&self) -> Option<String> {
358 self.inner().status_text.clone()
359 }
360
361 pub(crate) fn set_status_text(self, new_value: Option<String>) {
362 if self.inner().status_text == new_value {
363 return;
364 }
365 self.inner_mut().status_text = new_value.clone();
366 self.delegate().notify_status_text_changed(self, new_value);
367 }
368
369 pub fn page_title(&self) -> Option<String> {
375 self.inner().page_title.clone()
376 }
377
378 pub(crate) fn set_page_title(self, new_value: Option<String>) {
379 if self.inner().page_title == new_value {
380 return;
381 }
382 self.inner_mut().page_title = new_value.clone();
383 self.delegate().notify_page_title_changed(self, new_value);
384 }
385
386 pub fn favicon(&self) -> Option<Ref<'_, Image>> {
393 Ref::filter_map(self.inner(), |inner| inner.favicon.as_ref()).ok()
394 }
395
396 pub(crate) fn set_favicon(self, new_value: Option<Image>) {
397 if new_value.is_none() && self.inner().favicon.is_none() {
398 return;
399 }
400 self.inner_mut().favicon = new_value;
401 self.delegate().notify_favicon_changed(self);
402 }
403
404 pub fn focused(&self) -> bool {
406 self.inner().focused
407 }
408
409 pub fn cursor(&self) -> Cursor {
415 self.inner().cursor
416 }
417
418 pub(crate) fn set_cursor(self, new_value: Cursor) {
419 if self.inner().cursor == new_value {
420 return;
421 }
422 self.inner_mut().cursor = new_value;
423 self.delegate().notify_cursor_changed(self, new_value);
424 }
425
426 pub fn set_focused(&self, focused: bool) {
432 let old_focused = std::mem::replace(&mut self.inner_mut().focused, focused);
433 if old_focused != focused {
434 self.servo().constellation_proxy().send(
435 EmbedderToConstellationMessage::SetWebViewHasSystemFocus(self.id(), focused),
436 );
437 }
438 }
439
440 pub fn animating(&self) -> bool {
445 self.inner().animating
446 }
447
448 pub(crate) fn set_animating(self, new_value: bool) {
449 if self.inner().animating == new_value {
450 return;
451 }
452 self.inner_mut().animating = new_value;
453 self.delegate().notify_animating_changed(self, new_value);
454 }
455
456 pub fn size(&self) -> DeviceSize {
458 self.inner().rendering_context.size2d().to_f32()
459 }
460
461 pub fn resize(&self, new_size: PhysicalSize<u32>) {
468 let new_size = PhysicalSize {
469 width: new_size.width.max(MINIMUM_WEBVIEW_SIZE.width as u32),
470 height: new_size.height.max(MINIMUM_WEBVIEW_SIZE.height as u32),
471 };
472
473 self.servo()
474 .paint()
475 .resize_rendering_context(self.id(), new_size);
476 }
477
478 pub fn hidpi_scale_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel> {
480 self.inner().hidpi_scale_factor
481 }
482
483 pub fn set_hidpi_scale_factor(
488 &self,
489 new_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
490 ) {
491 if self.inner().hidpi_scale_factor == new_scale_factor {
492 return;
493 }
494
495 self.inner_mut().hidpi_scale_factor = new_scale_factor;
496 self.servo()
497 .paint()
498 .set_hidpi_scale_factor(self.id(), new_scale_factor);
499 }
500
501 pub fn show(&self) {
503 self.servo()
504 .paint()
505 .show_webview(self.id())
506 .expect("BUG: invalid WebView instance");
507 }
508
509 pub fn hide(&self) {
511 self.servo()
512 .paint()
513 .hide_webview(self.id())
514 .expect("BUG: invalid WebView instance");
515 }
516
517 pub fn notify_theme_change(&self, theme: Theme) {
519 self.servo()
520 .constellation_proxy()
521 .send(EmbedderToConstellationMessage::ThemeChange(
522 self.id(),
523 theme,
524 ))
525 }
526
527 pub fn load(&self, url: Url) {
532 self.servo()
533 .constellation_proxy()
534 .send(EmbedderToConstellationMessage::LoadUrl(
535 self.id(),
536 UrlRequest::new(url),
537 ))
538 }
539
540 pub fn load_request(&self, url_request: UrlRequest) {
545 self.servo()
546 .constellation_proxy()
547 .send(EmbedderToConstellationMessage::LoadUrl(
548 self.id(),
549 url_request,
550 ))
551 }
552
553 pub fn reload(&self) {
555 self.inner_mut().load_status = LoadStatus::Started;
556 self.servo()
557 .constellation_proxy()
558 .send(EmbedderToConstellationMessage::Reload(self.id()))
559 }
560
561 pub fn can_go_back(&self) -> bool {
566 self.inner().back_forward_list_index != 0
567 }
568
569 pub fn go_back(&self, amount: usize) -> TraversalId {
575 let request = SessionHistoryTraversalRequest::new(
576 self.id(),
577 TraversalDirection::Back(amount),
578 HistoryTraversalSource::Embedder,
579 );
580 let traversal_id = request.id.clone();
581 self.servo()
582 .constellation_proxy()
583 .send(EmbedderToConstellationMessage::TraverseHistory(request));
584 traversal_id
585 }
586
587 pub fn can_go_forward(&self) -> bool {
592 let inner = self.inner();
593 inner.back_forward_list.len() > inner.back_forward_list_index + 1
594 }
595
596 pub fn go_forward(&self, amount: usize) -> TraversalId {
602 let request = SessionHistoryTraversalRequest::new(
603 self.id(),
604 TraversalDirection::Forward(amount),
605 HistoryTraversalSource::Embedder,
606 );
607 let traversal_id = request.id.clone();
608 self.servo()
609 .constellation_proxy()
610 .send(EmbedderToConstellationMessage::TraverseHistory(request));
611 traversal_id
612 }
613
614 pub fn notify_scroll_event(&self, scroll: Scroll, point: WebViewPoint) {
617 self.servo()
618 .paint()
619 .notify_scroll_event(self.id(), scroll, point);
620 }
621
622 pub fn notify_input_event(&self, event: InputEvent) -> InputEventId {
629 let event: InputEventAndId = event.into();
630 let event_id = event.id;
631 let webview_id = self.id();
632 let servo = &self.inner().servo;
633
634 if event.event.point().is_some() {
636 if !servo.paint().notify_input_event(self.id(), event) {
637 servo.add_pending_handled_input_event(PendingHandledInputEvent {
638 event_id,
639 webview_id,
640 });
641 servo.event_loop_waker().wake();
642 }
643 } else {
644 servo
645 .constellation_proxy()
646 .send(EmbedderToConstellationMessage::ForwardInputEvent(
647 webview_id, event, None, ));
649 }
650
651 event_id
652 }
653
654 pub fn notify_media_session_action_event(&self, event: MediaSessionActionType) {
656 self.servo()
657 .constellation_proxy()
658 .send(EmbedderToConstellationMessage::MediaSessionAction(event));
659 }
660
661 pub fn set_page_zoom(&self, new_zoom: f32) {
671 self.servo().paint().set_page_zoom(self.id(), new_zoom);
672 }
673
674 pub fn page_zoom(&self) -> f32 {
676 self.servo().paint().page_zoom(self.id())
677 }
678
679 pub fn adjust_pinch_zoom(&self, pinch_zoom_delta: f32, center: DevicePoint) {
690 self.servo()
691 .paint()
692 .adjust_pinch_zoom(self.id(), pinch_zoom_delta, center);
693 }
694
695 pub fn pinch_zoom(&self) -> f32 {
697 self.servo().paint().pinch_zoom(self.id())
698 }
699
700 pub fn device_pixels_per_css_pixel(&self) -> Scale<f32, CSSPixel, DevicePixel> {
705 self.servo().paint().device_pixels_per_page_pixel(self.id())
706 }
707
708 pub fn exit_fullscreen(&self) {
710 self.servo()
711 .constellation_proxy()
712 .send(EmbedderToConstellationMessage::ExitFullScreen(self.id()));
713 }
714
715 pub fn toggle_webrender_debugging(&self, debugging: WebRenderDebugOption) {
721 self.servo().paint().toggle_webrender_debug(debugging);
722 }
723
724 pub fn capture_webrender(&self) {
729 self.servo().paint().capture_webrender(self.id());
730 }
731
732 pub fn paint(&self) {
734 self.servo().paint().render(self.id());
735 }
736
737 pub fn user_content_manager(&self) -> Option<Rc<UserContentManager>> {
739 self.inner().user_content_manager.clone()
740 }
741
742 pub fn evaluate_javascript<T: ToString>(
745 &self,
746 script: T,
747 callback: impl FnOnce(Result<JSValue, JavaScriptEvaluationError>) + 'static,
748 ) {
749 self.servo().javascript_evaluator_mut().evaluate(
750 self.id(),
751 script.to_string(),
752 Box::new(callback),
753 );
754 }
755
756 pub fn take_screenshot(
774 &self,
775 rect: Option<WebViewRect>,
776 callback: impl FnOnce(Result<RgbaImage, ScreenshotCaptureError>) + 'static,
777 ) {
778 self.servo()
779 .paint()
780 .request_screenshot(self.id(), rect, Box::new(callback));
781 }
782
783 pub(crate) fn set_history(self, new_back_forward_list: Vec<ServoUrl>, new_index: usize) {
784 {
785 let mut inner_mut = self.inner_mut();
786 inner_mut.back_forward_list_index = new_index;
787 inner_mut.back_forward_list = new_back_forward_list
788 .into_iter()
789 .map(ServoUrl::into_url)
790 .collect();
791 }
792
793 let back_forward_list = self.inner().back_forward_list.clone();
794 let back_forward_list_index = self.inner().back_forward_list_index;
795 self.delegate().notify_url_changed(
796 self.clone(),
797 back_forward_list[back_forward_list_index].clone(),
798 );
799 self.delegate().notify_history_changed(
800 self.clone(),
801 back_forward_list,
802 back_forward_list_index,
803 );
804 }
805
806 pub(crate) fn show_embedder_control(
807 self,
808 control_id: EmbedderControlId,
809 position: DeviceIntRect,
810 embedder_control_request: EmbedderControlRequest,
811 ) {
812 let constellation_proxy = self.servo().constellation_proxy().clone();
813 let embedder_control = match embedder_control_request {
814 EmbedderControlRequest::SelectElement(request) => {
815 EmbedderControl::SelectElement(SelectElement {
816 id: control_id,
817 select_element_request: request,
818 position,
819 constellation_proxy,
820 response_sent: false,
821 })
822 },
823 EmbedderControlRequest::ColorPicker(current_color) => {
824 EmbedderControl::ColorPicker(ColorPicker {
825 id: control_id,
826 current_color: Some(current_color),
827 position,
828 constellation_proxy,
829 response_sent: false,
830 })
831 },
832 EmbedderControlRequest::InputMethod(input_method_request) => {
833 EmbedderControl::InputMethod(InputMethodControl {
834 id: control_id,
835 input_method_type: input_method_request.input_method_type,
836 text: input_method_request.text,
837 insertion_point: input_method_request.insertion_point,
838 position,
839 multiline: input_method_request.multiline,
840 allow_virtual_keyboard: input_method_request.allow_virtual_keyboard,
841 })
842 },
843 EmbedderControlRequest::ContextMenu(mut context_menu_request) => {
844 for item in context_menu_request.items.iter_mut() {
845 match item {
846 ContextMenuItem::Item {
847 action: ContextMenuAction::GoBack,
848 enabled,
849 ..
850 } => *enabled = self.can_go_back(),
851 ContextMenuItem::Item {
852 action: ContextMenuAction::GoForward,
853 enabled,
854 ..
855 } => *enabled = self.can_go_forward(),
856 _ => {},
857 }
858 }
859 EmbedderControl::ContextMenu(ContextMenu {
860 id: control_id,
861 position,
862 items: context_menu_request.items,
863 element_info: context_menu_request.element_info,
864 constellation_proxy,
865 response_sent: false,
866 })
867 },
868 EmbedderControlRequest::FilePicker { .. } => {
869 unreachable!("This message should be routed through the FileManagerThread")
870 },
871 };
872
873 self.delegate()
874 .show_embedder_control(self.clone(), embedder_control);
875 }
876
877 pub fn accesskit_tree_id(&self) -> Option<TreeId> {
879 self.inner().accesskit_tree_id
880 }
881
882 pub fn set_accessibility_active(&self, active: bool) -> Option<TreeId> {
897 if !pref!(accessibility_enabled) {
898 return None;
899 }
900
901 if active == self.inner().accesskit_tree_id.is_some() {
902 return self.accesskit_tree_id();
903 }
904
905 if active {
906 let accesskit_tree_id = TreeId(AccesskitUuid::new_v4());
907 self.inner_mut().accesskit_tree_id = Some(accesskit_tree_id);
908 } else {
909 self.inner_mut().accesskit_tree_id = None;
910 self.inner_mut().grafted_accesskit_tree_id = None;
911 self.inner_mut().grafted_accesskit_tree_epoch = None;
912 }
913
914 self.send_accessibility_root_node();
915
916 self.servo().constellation_proxy().send(
917 EmbedderToConstellationMessage::SetAccessibilityActive(self.id(), active),
918 );
919
920 self.accesskit_tree_id()
921 }
922
923 pub(crate) fn notify_document_accessibility_tree_id(&self, grafted_tree_id: TreeId) {
924 let old_grafted_tree_id = self
925 .inner_mut()
926 .grafted_accesskit_tree_id
927 .replace(grafted_tree_id);
928 if old_grafted_tree_id == Some(grafted_tree_id) {
931 return;
932 }
933 self.send_accessibility_root_node();
934 }
935
936 pub(crate) fn note_accessibility_viewport_changed(&self) {
945 self.inner().accessibility_viewport_changed.set(true);
946 }
947
948 pub(crate) fn take_accessibility_viewport_changed(&self) -> bool {
950 self.inner().accessibility_viewport_changed.take()
951 }
952
953 pub(crate) fn send_accessibility_root_node(&self) {
960 let Some(webview_accesskit_tree_id) = self.inner().accesskit_tree_id else {
961 return;
962 };
963 let Some(grafted_tree_id) = self.inner().grafted_accesskit_tree_id else {
964 return;
965 };
966 let root_node_id = NodeId(0);
967 let mut root_node = AccesskitNode::new(Role::ScrollView);
968 let device_pixels_per_css_pixel =
981 self.page_zoom() * self.hidpi_scale_factor().get() * self.pinch_zoom();
982 let size =
983 self.size() / Scale::<f32, CSSPixel, DevicePixel>::new(device_pixels_per_css_pixel);
984 root_node.set_bounds(AccesskitRect::new(
985 0.0,
986 0.0,
987 size.width as f64,
988 size.height as f64,
989 ));
990 if device_pixels_per_css_pixel != 1.0 {
992 root_node.set_transform(AccesskitAffine::scale(device_pixels_per_css_pixel as f64));
993 }
994 let graft_node_id = NodeId(1);
995 let mut graft_node = AccesskitNode::new(Role::GenericContainer);
996 graft_node.set_tree_id(grafted_tree_id);
997 root_node.set_children(vec![graft_node_id]);
998 self.delegate().notify_accessibility_tree_update(
999 self.clone(),
1000 TreeUpdate {
1001 nodes: vec![(root_node_id, root_node), (graft_node_id, graft_node)],
1002 tree: Some(Tree {
1003 root: root_node_id,
1004 toolkit_name: None,
1005 toolkit_version: None,
1006 }),
1007 tree_id: webview_accesskit_tree_id,
1008 focus: root_node_id,
1009 },
1010 );
1011 }
1012
1013 fn move_accessibility_focus_to_graft_node(&self) {
1016 let Some(webview_accesskit_tree_id) = self.inner().accesskit_tree_id else {
1017 return;
1018 };
1019 let root_node_id = NodeId(0);
1020 let graft_node_id = NodeId(1);
1021
1022 self.delegate().notify_accessibility_tree_update(
1023 self.clone(),
1024 TreeUpdate {
1025 nodes: vec![],
1026 tree: Some(Tree {
1027 root: root_node_id,
1028 toolkit_name: None,
1029 toolkit_version: None,
1030 }),
1031 tree_id: webview_accesskit_tree_id,
1032 focus: graft_node_id,
1033 },
1034 );
1035 }
1036
1037 pub(crate) fn process_accessibility_tree_update(&self, tree_update: TreeUpdate, epoch: Epoch) {
1038 if self
1039 .inner()
1040 .grafted_accesskit_tree_epoch
1041 .is_some_and(|current| epoch < current)
1042 {
1043 debug!("Ignoring stale tree update for {:?}", tree_update.tree_id);
1047 return;
1048 }
1049 if self
1050 .inner()
1051 .grafted_accesskit_tree_epoch
1052 .is_none_or(|current| epoch > current)
1053 {
1054 self.notify_document_accessibility_tree_id(tree_update.tree_id);
1058 self.inner_mut().grafted_accesskit_tree_epoch = Some(epoch);
1059
1060 self.delegate()
1061 .notify_accessibility_tree_update(self.clone(), tree_update);
1062
1063 self.move_accessibility_focus_to_graft_node();
1064 return;
1065 }
1066
1067 self.delegate()
1068 .notify_accessibility_tree_update(self.clone(), tree_update);
1069 }
1070
1071 pub fn clear_session_history(&self) {
1076 self.servo().constellation_proxy().send(
1077 EmbedderToConstellationMessage::ClearSessionHistory(self.id()),
1078 );
1079 }
1080}
1081
1082struct ServoRendererWebView {
1085 id: WebViewId,
1086 weak_handle: Weak<RefCell<WebViewInner>>,
1087}
1088
1089impl WebViewTrait for ServoRendererWebView {
1090 fn id(&self) -> WebViewId {
1091 self.id
1092 }
1093
1094 fn screen_geometry(&self) -> Option<ScreenGeometry> {
1095 let webview = WebView::from_weak_handle(&self.weak_handle)?;
1096 webview.delegate().screen_geometry(webview)
1097 }
1098
1099 fn set_animating(&self, new_value: bool) {
1100 if let Some(webview) = WebView::from_weak_handle(&self.weak_handle) {
1101 webview.set_animating(new_value);
1102 }
1103 }
1104
1105 fn notify_viewport_updated(&self) {
1106 if let Some(webview) = WebView::from_weak_handle(&self.weak_handle) {
1107 webview.note_accessibility_viewport_changed();
1108 }
1109 }
1110}
1111
1112pub struct WebViewBuilder {
1114 servo: Servo,
1115 rendering_context: Rc<dyn RenderingContext>,
1116 delegate: Rc<dyn WebViewDelegate>,
1117 url: Option<Url>,
1118 hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
1119 create_new_webview_responder: Option<AutomaticResponder<Option<NewWebViewDetails>>>,
1120 user_content_manager: Option<Rc<UserContentManager>>,
1121 clipboard_delegate: Option<Rc<dyn ClipboardDelegate>>,
1122 #[cfg(feature = "gamepad")]
1123 gamepad_delegate: Option<Rc<dyn GamepadDelegate>>,
1124 geolocation_delegate: Option<Rc<dyn GeolocationDelegate>>,
1125}
1126
1127impl WebViewBuilder {
1128 pub fn new(servo: &Servo, rendering_context: Rc<dyn RenderingContext>) -> Self {
1133 Self {
1134 servo: servo.clone(),
1135 rendering_context,
1136 url: None,
1137 hidpi_scale_factor: Scale::new(1.0),
1138 delegate: Rc::new(DefaultWebViewDelegate),
1139 create_new_webview_responder: None,
1140 user_content_manager: None,
1141 clipboard_delegate: None,
1142 #[cfg(feature = "gamepad")]
1143 gamepad_delegate: None,
1144 geolocation_delegate: None,
1145 }
1146 }
1147
1148 pub(crate) fn new_for_create_request(
1149 servo: &Servo,
1150 rendering_context: Rc<dyn RenderingContext>,
1151 responder: AutomaticResponder<Option<NewWebViewDetails>>,
1152 ) -> Self {
1153 let mut builder = Self::new(servo, rendering_context);
1154 builder.create_new_webview_responder = Some(responder);
1155 builder
1156 }
1157
1158 pub fn delegate(mut self, delegate: Rc<dyn WebViewDelegate>) -> Self {
1161 self.delegate = delegate;
1162 self
1163 }
1164
1165 pub fn url(mut self, url: Url) -> Self {
1167 self.url = Some(url);
1168 self
1169 }
1170
1171 pub fn hidpi_scale_factor(
1173 mut self,
1174 hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
1175 ) -> Self {
1176 self.hidpi_scale_factor = hidpi_scale_factor;
1177 self
1178 }
1179
1180 pub fn user_content_manager(mut self, user_content_manager: Rc<UserContentManager>) -> Self {
1184 self.user_content_manager = Some(user_content_manager);
1185 self
1186 }
1187
1188 pub fn clipboard_delegate(mut self, clipboard_delegate: Rc<dyn ClipboardDelegate>) -> Self {
1191 self.clipboard_delegate = Some(clipboard_delegate);
1192 self
1193 }
1194
1195 #[cfg(feature = "gamepad")]
1198 pub fn gamepad_delegate(mut self, gamepad_delegate: Rc<dyn GamepadDelegate>) -> Self {
1199 self.gamepad_delegate = Some(gamepad_delegate);
1200 self
1201 }
1202
1203 pub fn geolocation_delegate(
1206 mut self,
1207 geolocation_delegate: Rc<dyn GeolocationDelegate>,
1208 ) -> Self {
1209 self.geolocation_delegate = Some(geolocation_delegate);
1210 self
1211 }
1212
1213 pub fn build(self) -> WebView {
1215 WebView::new(self)
1216 }
1217}