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::responders::AutomaticResponder;
43use crate::servo::PendingHandledInputEvent;
44use crate::webview_delegate::{CreateNewWebViewRequest, DefaultWebViewDelegate, WebViewDelegate};
45use crate::{
46 ColorPicker, ContextMenu, EmbedderControl, InputMethodControl, SelectElement, Servo,
47 UserContentManager, WebRenderDebugOption,
48};
49
50pub(crate) const MINIMUM_WEBVIEW_SIZE: Size2D<i32, DevicePixel> = Size2D::new(1, 1);
51
52#[derive(Clone)]
84pub struct WebView(Rc<RefCell<WebViewInner>>);
85
86impl PartialEq for WebView {
87 fn eq(&self, other: &Self) -> bool {
88 self.inner().id == other.inner().id
89 }
90}
91
92impl Hash for WebView {
93 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
94 self.inner().id.hash(state);
95 }
96}
97
98pub(crate) struct WebViewInner {
99 pub(crate) id: WebViewId,
100 pub(crate) servo: Servo,
101 pub(crate) delegate: Rc<dyn WebViewDelegate>,
102 pub(crate) clipboard_delegate: Rc<dyn ClipboardDelegate>,
103 #[cfg(feature = "gamepad")]
104 pub(crate) gamepad_delegate: Rc<dyn GamepadDelegate>,
105
106 pub(crate) accesskit_tree_id: Option<TreeId>,
111 pub(crate) grafted_accesskit_tree_id: Option<TreeId>,
114 grafted_accesskit_tree_epoch: Option<Epoch>,
117 accessibility_viewport_changed: Cell<bool>,
125
126 rendering_context: Rc<dyn RenderingContext>,
127 user_content_manager: Option<Rc<UserContentManager>>,
128 hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
129 load_status: LoadStatus,
130 status_text: Option<String>,
131 page_title: Option<String>,
132 favicon: Option<Image>,
133 focused: bool,
134 animating: bool,
135 cursor: Cursor,
136
137 back_forward_list: Vec<Url>,
139
140 back_forward_list_index: usize,
142}
143
144impl Drop for WebViewInner {
145 fn drop(&mut self) {
146 self.servo
147 .constellation_proxy()
148 .send(EmbedderToConstellationMessage::CloseWebView(self.id));
149 self.servo.paint_mut().remove_webview(self.id);
150 }
151}
152
153impl WebView {
154 pub(crate) fn new(mut builder: WebViewBuilder) -> Self {
155 let servo = builder.servo;
156 let painter_id = servo
157 .paint_mut()
158 .register_rendering_context(builder.rendering_context.clone());
159
160 let id = WebViewId::new(painter_id);
161 let webview = Self(Rc::new(RefCell::new(WebViewInner {
162 id,
163 servo: servo.clone(),
164 rendering_context: builder.rendering_context,
165 delegate: builder.delegate,
166 clipboard_delegate: builder
167 .clipboard_delegate
168 .unwrap_or_else(|| Rc::new(DefaultClipboardDelegate)),
169 #[cfg(feature = "gamepad")]
170 gamepad_delegate: builder
171 .gamepad_delegate
172 .unwrap_or_else(|| Rc::new(DefaultGamepadDelegate)),
173 accesskit_tree_id: None,
174 grafted_accesskit_tree_id: None,
175 grafted_accesskit_tree_epoch: None,
176 accessibility_viewport_changed: Cell::new(false),
177 hidpi_scale_factor: builder.hidpi_scale_factor,
178 load_status: LoadStatus::Started,
179 status_text: None,
180 page_title: None,
181 favicon: None,
182 focused: true,
183 animating: false,
184 cursor: Cursor::Pointer,
185 back_forward_list: Default::default(),
186 back_forward_list_index: 0,
187 user_content_manager: builder.user_content_manager.clone(),
188 })));
189
190 let viewport_details = webview.viewport_details();
191 servo.paint().add_webview(
192 Box::new(ServoRendererWebView {
193 weak_handle: webview.weak_handle(),
194 id,
195 }),
196 viewport_details,
197 );
198
199 servo
200 .webviews_mut()
201 .insert(webview.id(), webview.weak_handle());
202
203 let user_content_manager_id = builder
204 .user_content_manager
205 .as_ref()
206 .map(|user_content_manager| user_content_manager.id());
207
208 let new_webview_details = NewWebViewDetails {
209 webview_id: webview.id(),
210 viewport_details,
211 user_content_manager_id,
212 };
213
214 match builder.create_new_webview_responder.as_mut() {
220 Some(responder) => {
221 let _ = responder.send(Some(new_webview_details));
222 },
223 None => {
224 let url = builder.url.unwrap_or(
225 Url::parse("about:blank")
226 .expect("Should always be able to parse 'about:blank'."),
227 );
228
229 servo
230 .constellation_proxy()
231 .send(EmbedderToConstellationMessage::NewWebView(
232 url.into(),
233 new_webview_details,
234 ));
235 },
236 }
237
238 webview
239 }
240
241 fn inner(&self) -> Ref<'_, WebViewInner> {
242 self.0.borrow()
243 }
244
245 fn inner_mut(&self) -> RefMut<'_, WebViewInner> {
246 self.0.borrow_mut()
247 }
248
249 fn servo(&self) -> Servo {
250 self.0.borrow().servo.clone()
251 }
252
253 pub(crate) fn request_create_new(
254 &self,
255 response_sender: GenericSender<Option<NewWebViewDetails>>,
256 ) {
257 let request = CreateNewWebViewRequest {
258 servo: self.servo(),
259 responder: AutomaticResponder::new(response_sender, None),
260 };
261 self.delegate().request_create_new(self.clone(), request);
262 }
263
264 pub(crate) fn viewport_details(&self) -> ViewportDetails {
265 let inner = self.inner();
268 let viewport_size = inner.rendering_context.size2d().to_f32();
269 let scaled_viewport_size = viewport_size / inner.hidpi_scale_factor;
270 let device_size = self
271 .delegate()
272 .screen_geometry(self.clone())
273 .map(|geometry| geometry.size.to_f32())
274 .unwrap_or_else(|| viewport_size);
275 ViewportDetails {
276 size: scaled_viewport_size / Scale::new(1.0),
277 hidpi_scale_factor: Scale::new(inner.hidpi_scale_factor.0),
278 device_size,
279 }
280 }
281
282 pub(crate) fn from_weak_handle(inner: &Weak<RefCell<WebViewInner>>) -> Option<Self> {
283 inner.upgrade().map(WebView)
284 }
285
286 pub(crate) fn weak_handle(&self) -> Weak<RefCell<WebViewInner>> {
287 Rc::downgrade(&self.0)
288 }
289
290 pub fn delegate(&self) -> Rc<dyn WebViewDelegate> {
292 self.inner().delegate.clone()
293 }
294
295 pub fn clipboard_delegate(&self) -> Rc<dyn ClipboardDelegate> {
297 self.inner().clipboard_delegate.clone()
298 }
299
300 #[cfg(feature = "gamepad")]
302 pub fn gamepad_delegate(&self) -> Rc<dyn GamepadDelegate> {
303 self.inner().gamepad_delegate.clone()
304 }
305
306 pub fn id(&self) -> WebViewId {
308 self.inner().id
309 }
310
311 pub fn rendering_context(&self) -> Rc<dyn RenderingContext> {
313 self.inner().rendering_context.clone()
314 }
315
316 pub fn load_status(&self) -> LoadStatus {
321 self.inner().load_status
322 }
323
324 pub(crate) fn set_load_status(self, new_value: LoadStatus) {
325 if self.inner().load_status == new_value {
326 return;
327 }
328 self.inner_mut().load_status = new_value;
329 self.delegate().notify_load_status_changed(self, new_value);
330 }
331
332 pub fn url(&self) -> Option<Url> {
335 let inner = self.inner();
336 inner
337 .back_forward_list
338 .get(inner.back_forward_list_index)
339 .cloned()
340 }
341
342 pub fn status_text(&self) -> Option<String> {
348 self.inner().status_text.clone()
349 }
350
351 pub(crate) fn set_status_text(self, new_value: Option<String>) {
352 if self.inner().status_text == new_value {
353 return;
354 }
355 self.inner_mut().status_text = new_value.clone();
356 self.delegate().notify_status_text_changed(self, new_value);
357 }
358
359 pub fn page_title(&self) -> Option<String> {
365 self.inner().page_title.clone()
366 }
367
368 pub(crate) fn set_page_title(self, new_value: Option<String>) {
369 if self.inner().page_title == new_value {
370 return;
371 }
372 self.inner_mut().page_title = new_value.clone();
373 self.delegate().notify_page_title_changed(self, new_value);
374 }
375
376 pub fn favicon(&self) -> Option<Ref<'_, Image>> {
383 Ref::filter_map(self.inner(), |inner| inner.favicon.as_ref()).ok()
384 }
385
386 pub(crate) fn set_favicon(self, new_value: Image) {
387 self.inner_mut().favicon = Some(new_value);
388 self.delegate().notify_favicon_changed(self);
389 }
390
391 pub fn focused(&self) -> bool {
393 self.inner().focused
394 }
395
396 pub fn cursor(&self) -> Cursor {
402 self.inner().cursor
403 }
404
405 pub(crate) fn set_cursor(self, new_value: Cursor) {
406 if self.inner().cursor == new_value {
407 return;
408 }
409 self.inner_mut().cursor = new_value;
410 self.delegate().notify_cursor_changed(self, new_value);
411 }
412
413 pub fn set_focused(&self, focused: bool) {
419 let old_focused = std::mem::replace(&mut self.inner_mut().focused, focused);
420 if old_focused != focused {
421 self.servo().constellation_proxy().send(
422 EmbedderToConstellationMessage::SetWebViewHasSystemFocus(self.id(), focused),
423 );
424 }
425 }
426
427 pub fn animating(&self) -> bool {
432 self.inner().animating
433 }
434
435 pub(crate) fn set_animating(self, new_value: bool) {
436 if self.inner().animating == new_value {
437 return;
438 }
439 self.inner_mut().animating = new_value;
440 self.delegate().notify_animating_changed(self, new_value);
441 }
442
443 pub fn size(&self) -> DeviceSize {
445 self.inner().rendering_context.size2d().to_f32()
446 }
447
448 pub fn resize(&self, new_size: PhysicalSize<u32>) {
455 let new_size = PhysicalSize {
456 width: new_size.width.max(MINIMUM_WEBVIEW_SIZE.width as u32),
457 height: new_size.height.max(MINIMUM_WEBVIEW_SIZE.height as u32),
458 };
459
460 self.servo()
461 .paint()
462 .resize_rendering_context(self.id(), new_size);
463 }
464
465 pub fn hidpi_scale_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel> {
467 self.inner().hidpi_scale_factor
468 }
469
470 pub fn set_hidpi_scale_factor(
475 &self,
476 new_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
477 ) {
478 if self.inner().hidpi_scale_factor == new_scale_factor {
479 return;
480 }
481
482 self.inner_mut().hidpi_scale_factor = new_scale_factor;
483 self.servo()
484 .paint()
485 .set_hidpi_scale_factor(self.id(), new_scale_factor);
486 }
487
488 pub fn show(&self) {
490 self.servo()
491 .paint()
492 .show_webview(self.id())
493 .expect("BUG: invalid WebView instance");
494 }
495
496 pub fn hide(&self) {
498 self.servo()
499 .paint()
500 .hide_webview(self.id())
501 .expect("BUG: invalid WebView instance");
502 }
503
504 pub fn notify_theme_change(&self, theme: Theme) {
506 self.servo()
507 .constellation_proxy()
508 .send(EmbedderToConstellationMessage::ThemeChange(
509 self.id(),
510 theme,
511 ))
512 }
513
514 pub fn load(&self, url: Url) {
519 self.servo()
520 .constellation_proxy()
521 .send(EmbedderToConstellationMessage::LoadUrl(
522 self.id(),
523 UrlRequest::new(url),
524 ))
525 }
526
527 pub fn load_request(&self, url_request: UrlRequest) {
532 self.servo()
533 .constellation_proxy()
534 .send(EmbedderToConstellationMessage::LoadUrl(
535 self.id(),
536 url_request,
537 ))
538 }
539
540 pub fn reload(&self) {
542 self.inner_mut().load_status = LoadStatus::Started;
543 self.servo()
544 .constellation_proxy()
545 .send(EmbedderToConstellationMessage::Reload(self.id()))
546 }
547
548 pub fn can_go_back(&self) -> bool {
553 self.inner().back_forward_list_index != 0
554 }
555
556 pub fn go_back(&self, amount: usize) -> TraversalId {
562 let request = SessionHistoryTraversalRequest::new(
563 self.id(),
564 TraversalDirection::Back(amount),
565 HistoryTraversalSource::Embedder,
566 );
567 let traversal_id = request.id.clone();
568 self.servo()
569 .constellation_proxy()
570 .send(EmbedderToConstellationMessage::TraverseHistory(request));
571 traversal_id
572 }
573
574 pub fn can_go_forward(&self) -> bool {
579 let inner = self.inner();
580 inner.back_forward_list.len() > inner.back_forward_list_index + 1
581 }
582
583 pub fn go_forward(&self, amount: usize) -> TraversalId {
589 let request = SessionHistoryTraversalRequest::new(
590 self.id(),
591 TraversalDirection::Forward(amount),
592 HistoryTraversalSource::Embedder,
593 );
594 let traversal_id = request.id.clone();
595 self.servo()
596 .constellation_proxy()
597 .send(EmbedderToConstellationMessage::TraverseHistory(request));
598 traversal_id
599 }
600
601 pub fn notify_scroll_event(&self, scroll: Scroll, point: WebViewPoint) {
604 self.servo()
605 .paint()
606 .notify_scroll_event(self.id(), scroll, point);
607 }
608
609 pub fn notify_input_event(&self, event: InputEvent) -> InputEventId {
616 let event: InputEventAndId = event.into();
617 let event_id = event.id;
618 let webview_id = self.id();
619 let servo = &self.inner().servo;
620
621 if event.event.point().is_some() {
623 if !servo.paint().notify_input_event(self.id(), event) {
624 servo.add_pending_handled_input_event(PendingHandledInputEvent {
625 event_id,
626 webview_id,
627 });
628 servo.event_loop_waker().wake();
629 }
630 } else {
631 servo
632 .constellation_proxy()
633 .send(EmbedderToConstellationMessage::ForwardInputEvent(
634 webview_id, event, None, ));
636 }
637
638 event_id
639 }
640
641 pub fn notify_media_session_action_event(&self, event: MediaSessionActionType) {
643 self.servo()
644 .constellation_proxy()
645 .send(EmbedderToConstellationMessage::MediaSessionAction(event));
646 }
647
648 pub fn set_page_zoom(&self, new_zoom: f32) {
658 self.servo().paint().set_page_zoom(self.id(), new_zoom);
659 }
660
661 pub fn page_zoom(&self) -> f32 {
663 self.servo().paint().page_zoom(self.id())
664 }
665
666 pub fn adjust_pinch_zoom(&self, pinch_zoom_delta: f32, center: DevicePoint) {
677 self.servo()
678 .paint()
679 .adjust_pinch_zoom(self.id(), pinch_zoom_delta, center);
680 }
681
682 pub fn pinch_zoom(&self) -> f32 {
684 self.servo().paint().pinch_zoom(self.id())
685 }
686
687 pub fn device_pixels_per_css_pixel(&self) -> Scale<f32, CSSPixel, DevicePixel> {
692 self.servo().paint().device_pixels_per_page_pixel(self.id())
693 }
694
695 pub fn exit_fullscreen(&self) {
697 self.servo()
698 .constellation_proxy()
699 .send(EmbedderToConstellationMessage::ExitFullScreen(self.id()));
700 }
701
702 pub fn toggle_webrender_debugging(&self, debugging: WebRenderDebugOption) {
708 self.servo().paint().toggle_webrender_debug(debugging);
709 }
710
711 pub fn capture_webrender(&self) {
716 self.servo().paint().capture_webrender(self.id());
717 }
718
719 pub fn paint(&self) {
721 self.servo().paint().render(self.id());
722 }
723
724 pub fn user_content_manager(&self) -> Option<Rc<UserContentManager>> {
726 self.inner().user_content_manager.clone()
727 }
728
729 pub fn evaluate_javascript<T: ToString>(
732 &self,
733 script: T,
734 callback: impl FnOnce(Result<JSValue, JavaScriptEvaluationError>) + 'static,
735 ) {
736 self.servo().javascript_evaluator_mut().evaluate(
737 self.id(),
738 script.to_string(),
739 Box::new(callback),
740 );
741 }
742
743 pub fn take_screenshot(
761 &self,
762 rect: Option<WebViewRect>,
763 callback: impl FnOnce(Result<RgbaImage, ScreenshotCaptureError>) + 'static,
764 ) {
765 self.servo()
766 .paint()
767 .request_screenshot(self.id(), rect, Box::new(callback));
768 }
769
770 pub(crate) fn set_history(self, new_back_forward_list: Vec<ServoUrl>, new_index: usize) {
771 {
772 let mut inner_mut = self.inner_mut();
773 inner_mut.back_forward_list_index = new_index;
774 inner_mut.back_forward_list = new_back_forward_list
775 .into_iter()
776 .map(ServoUrl::into_url)
777 .collect();
778 }
779
780 let back_forward_list = self.inner().back_forward_list.clone();
781 let back_forward_list_index = self.inner().back_forward_list_index;
782 self.delegate().notify_url_changed(
783 self.clone(),
784 back_forward_list[back_forward_list_index].clone(),
785 );
786 self.delegate().notify_history_changed(
787 self.clone(),
788 back_forward_list,
789 back_forward_list_index,
790 );
791 }
792
793 pub(crate) fn show_embedder_control(
794 self,
795 control_id: EmbedderControlId,
796 position: DeviceIntRect,
797 embedder_control_request: EmbedderControlRequest,
798 ) {
799 let constellation_proxy = self.servo().constellation_proxy().clone();
800 let embedder_control = match embedder_control_request {
801 EmbedderControlRequest::SelectElement(request) => {
802 EmbedderControl::SelectElement(SelectElement {
803 id: control_id,
804 select_element_request: request,
805 position,
806 constellation_proxy,
807 response_sent: false,
808 })
809 },
810 EmbedderControlRequest::ColorPicker(current_color) => {
811 EmbedderControl::ColorPicker(ColorPicker {
812 id: control_id,
813 current_color: Some(current_color),
814 position,
815 constellation_proxy,
816 response_sent: false,
817 })
818 },
819 EmbedderControlRequest::InputMethod(input_method_request) => {
820 EmbedderControl::InputMethod(InputMethodControl {
821 id: control_id,
822 input_method_type: input_method_request.input_method_type,
823 text: input_method_request.text,
824 insertion_point: input_method_request.insertion_point,
825 position,
826 multiline: input_method_request.multiline,
827 allow_virtual_keyboard: input_method_request.allow_virtual_keyboard,
828 })
829 },
830 EmbedderControlRequest::ContextMenu(mut context_menu_request) => {
831 for item in context_menu_request.items.iter_mut() {
832 match item {
833 ContextMenuItem::Item {
834 action: ContextMenuAction::GoBack,
835 enabled,
836 ..
837 } => *enabled = self.can_go_back(),
838 ContextMenuItem::Item {
839 action: ContextMenuAction::GoForward,
840 enabled,
841 ..
842 } => *enabled = self.can_go_forward(),
843 _ => {},
844 }
845 }
846 EmbedderControl::ContextMenu(ContextMenu {
847 id: control_id,
848 position,
849 items: context_menu_request.items,
850 element_info: context_menu_request.element_info,
851 constellation_proxy,
852 response_sent: false,
853 })
854 },
855 EmbedderControlRequest::FilePicker { .. } => {
856 unreachable!("This message should be routed through the FileManagerThread")
857 },
858 };
859
860 self.delegate()
861 .show_embedder_control(self.clone(), embedder_control);
862 }
863
864 pub fn accesskit_tree_id(&self) -> Option<TreeId> {
866 self.inner().accesskit_tree_id
867 }
868
869 pub fn set_accessibility_active(&self, active: bool) -> Option<TreeId> {
884 if !pref!(accessibility_enabled) {
885 return None;
886 }
887
888 if active == self.inner().accesskit_tree_id.is_some() {
889 return self.accesskit_tree_id();
890 }
891
892 if active {
893 let accesskit_tree_id = TreeId(AccesskitUuid::new_v4());
894 self.inner_mut().accesskit_tree_id = Some(accesskit_tree_id);
895 } else {
896 self.inner_mut().accesskit_tree_id = None;
897 self.inner_mut().grafted_accesskit_tree_id = None;
898 self.inner_mut().grafted_accesskit_tree_epoch = None;
899 }
900
901 self.servo().constellation_proxy().send(
902 EmbedderToConstellationMessage::SetAccessibilityActive(self.id(), active),
903 );
904
905 self.accesskit_tree_id()
906 }
907
908 pub(crate) fn notify_document_accessibility_tree_id(&self, grafted_tree_id: TreeId) {
909 let old_grafted_tree_id = self
910 .inner_mut()
911 .grafted_accesskit_tree_id
912 .replace(grafted_tree_id);
913 if old_grafted_tree_id == Some(grafted_tree_id) {
916 return;
917 }
918 self.send_accessibility_root_node();
919 }
920
921 pub(crate) fn note_accessibility_viewport_changed(&self) {
930 self.inner().accessibility_viewport_changed.set(true);
931 }
932
933 pub(crate) fn take_accessibility_viewport_changed(&self) -> bool {
935 self.inner().accessibility_viewport_changed.take()
936 }
937
938 pub(crate) fn send_accessibility_root_node(&self) {
945 let Some(webview_accesskit_tree_id) = self.inner().accesskit_tree_id else {
946 return;
947 };
948 let Some(grafted_tree_id) = self.inner().grafted_accesskit_tree_id else {
949 return;
950 };
951 let root_node_id = NodeId(0);
952 let mut root_node = AccesskitNode::new(Role::ScrollView);
953 let device_pixels_per_css_pixel =
966 self.page_zoom() * self.hidpi_scale_factor().get() * self.pinch_zoom();
967 let size =
968 self.size() / Scale::<f32, CSSPixel, DevicePixel>::new(device_pixels_per_css_pixel);
969 root_node.set_bounds(AccesskitRect::new(
970 0.0,
971 0.0,
972 size.width as f64,
973 size.height as f64,
974 ));
975 if device_pixels_per_css_pixel != 1.0 {
977 root_node.set_transform(AccesskitAffine::scale(device_pixels_per_css_pixel as f64));
978 }
979 let graft_node_id = NodeId(1);
980 let mut graft_node = AccesskitNode::new(Role::GenericContainer);
981 graft_node.set_tree_id(grafted_tree_id);
982 root_node.set_children(vec![graft_node_id]);
983 self.delegate().notify_accessibility_tree_update(
984 self.clone(),
985 TreeUpdate {
986 nodes: vec![(root_node_id, root_node), (graft_node_id, graft_node)],
987 tree: Some(Tree {
988 root: root_node_id,
989 toolkit_name: None,
990 toolkit_version: None,
991 }),
992 tree_id: webview_accesskit_tree_id,
993 focus: root_node_id,
994 },
995 );
996 }
997
998 pub(crate) fn process_accessibility_tree_update(&self, tree_update: TreeUpdate, epoch: Epoch) {
999 if self
1000 .inner()
1001 .grafted_accesskit_tree_epoch
1002 .is_some_and(|current| epoch < current)
1003 {
1004 debug!("Ignoring stale tree update for {:?}", tree_update.tree_id);
1008 return;
1009 }
1010 if self
1011 .inner()
1012 .grafted_accesskit_tree_epoch
1013 .is_none_or(|current| epoch > current)
1014 {
1015 self.notify_document_accessibility_tree_id(tree_update.tree_id);
1016 self.inner_mut().grafted_accesskit_tree_epoch = Some(epoch);
1017 }
1018 self.delegate()
1019 .notify_accessibility_tree_update(self.clone(), tree_update);
1020 }
1021
1022 pub fn clear_session_history(&self) {
1027 self.servo().constellation_proxy().send(
1028 EmbedderToConstellationMessage::ClearSessionHistory(self.id()),
1029 );
1030 }
1031}
1032
1033struct ServoRendererWebView {
1036 id: WebViewId,
1037 weak_handle: Weak<RefCell<WebViewInner>>,
1038}
1039
1040impl WebViewTrait for ServoRendererWebView {
1041 fn id(&self) -> WebViewId {
1042 self.id
1043 }
1044
1045 fn screen_geometry(&self) -> Option<ScreenGeometry> {
1046 let webview = WebView::from_weak_handle(&self.weak_handle)?;
1047 webview.delegate().screen_geometry(webview)
1048 }
1049
1050 fn set_animating(&self, new_value: bool) {
1051 if let Some(webview) = WebView::from_weak_handle(&self.weak_handle) {
1052 webview.set_animating(new_value);
1053 }
1054 }
1055
1056 fn notify_viewport_updated(&self) {
1057 if let Some(webview) = WebView::from_weak_handle(&self.weak_handle) {
1058 webview.note_accessibility_viewport_changed();
1059 }
1060 }
1061}
1062
1063pub struct WebViewBuilder {
1065 servo: Servo,
1066 rendering_context: Rc<dyn RenderingContext>,
1067 delegate: Rc<dyn WebViewDelegate>,
1068 url: Option<Url>,
1069 hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
1070 create_new_webview_responder: Option<AutomaticResponder<Option<NewWebViewDetails>>>,
1071 user_content_manager: Option<Rc<UserContentManager>>,
1072 clipboard_delegate: Option<Rc<dyn ClipboardDelegate>>,
1073 #[cfg(feature = "gamepad")]
1074 gamepad_delegate: Option<Rc<dyn GamepadDelegate>>,
1075}
1076
1077impl WebViewBuilder {
1078 pub fn new(servo: &Servo, rendering_context: Rc<dyn RenderingContext>) -> Self {
1083 Self {
1084 servo: servo.clone(),
1085 rendering_context,
1086 url: None,
1087 hidpi_scale_factor: Scale::new(1.0),
1088 delegate: Rc::new(DefaultWebViewDelegate),
1089 create_new_webview_responder: None,
1090 user_content_manager: None,
1091 clipboard_delegate: None,
1092 #[cfg(feature = "gamepad")]
1093 gamepad_delegate: None,
1094 }
1095 }
1096
1097 pub(crate) fn new_for_create_request(
1098 servo: &Servo,
1099 rendering_context: Rc<dyn RenderingContext>,
1100 responder: AutomaticResponder<Option<NewWebViewDetails>>,
1101 ) -> Self {
1102 let mut builder = Self::new(servo, rendering_context);
1103 builder.create_new_webview_responder = Some(responder);
1104 builder
1105 }
1106
1107 pub fn delegate(mut self, delegate: Rc<dyn WebViewDelegate>) -> Self {
1110 self.delegate = delegate;
1111 self
1112 }
1113
1114 pub fn url(mut self, url: Url) -> Self {
1116 self.url = Some(url);
1117 self
1118 }
1119
1120 pub fn hidpi_scale_factor(
1122 mut self,
1123 hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
1124 ) -> Self {
1125 self.hidpi_scale_factor = hidpi_scale_factor;
1126 self
1127 }
1128
1129 pub fn user_content_manager(mut self, user_content_manager: Rc<UserContentManager>) -> Self {
1133 self.user_content_manager = Some(user_content_manager);
1134 self
1135 }
1136
1137 pub fn clipboard_delegate(mut self, clipboard_delegate: Rc<dyn ClipboardDelegate>) -> Self {
1140 self.clipboard_delegate = Some(clipboard_delegate);
1141 self
1142 }
1143
1144 #[cfg(feature = "gamepad")]
1147 pub fn gamepad_delegate(mut self, gamepad_delegate: Rc<dyn GamepadDelegate>) -> Self {
1148 self.gamepad_delegate = Some(gamepad_delegate);
1149 self
1150 }
1151
1152 pub fn build(self) -> WebView {
1154 WebView::new(self)
1155 }
1156}