1use std::cell::{Ref, RefCell, RefMut};
6use std::hash::Hash;
7use std::rc::{Rc, Weak};
8use std::time::Duration;
9
10use accesskit::{
11 Node as AccesskitNode, NodeId, Role, Tree, 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::{EmbedderToConstellationMessage, TraversalDirection};
30use servo_geometry::DeviceIndependentPixel;
31use servo_url::ServoUrl;
32use style_traits::CSSPixel;
33use url::Url;
34use webrender_api::units::{DeviceIntRect, DevicePixel, DevicePoint, DeviceSize};
35
36use crate::clipboard_delegate::{ClipboardDelegate, DefaultClipboardDelegate};
37#[cfg(feature = "gamepad")]
38use crate::gamepad_delegate::{DefaultGamepadDelegate, GamepadDelegate};
39use crate::responders::IpcResponder;
40use crate::servo::PendingHandledInputEvent;
41use crate::webview_delegate::{CreateNewWebViewRequest, DefaultWebViewDelegate, WebViewDelegate};
42use crate::{
43 ColorPicker, ContextMenu, EmbedderControl, InputMethodControl, SelectElement, Servo,
44 UserContentManager, WebRenderDebugOption,
45};
46
47pub(crate) const MINIMUM_WEBVIEW_SIZE: Size2D<i32, DevicePixel> = Size2D::new(1, 1);
48
49#[derive(Clone)]
81pub struct WebView(Rc<RefCell<WebViewInner>>);
82
83impl PartialEq for WebView {
84 fn eq(&self, other: &Self) -> bool {
85 self.inner().id == other.inner().id
86 }
87}
88
89impl Hash for WebView {
90 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
91 self.inner().id.hash(state);
92 }
93}
94
95pub(crate) struct WebViewInner {
96 pub(crate) id: WebViewId,
97 pub(crate) servo: Servo,
98 pub(crate) delegate: Rc<dyn WebViewDelegate>,
99 pub(crate) clipboard_delegate: Rc<dyn ClipboardDelegate>,
100 #[cfg(feature = "gamepad")]
101 pub(crate) gamepad_delegate: Rc<dyn GamepadDelegate>,
102
103 pub(crate) accesskit_tree_id: Option<TreeId>,
108 pub(crate) grafted_accesskit_tree_id: Option<TreeId>,
111 grafted_accesskit_tree_epoch: Option<Epoch>,
114
115 rendering_context: Rc<dyn RenderingContext>,
116 user_content_manager: Option<Rc<UserContentManager>>,
117 hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
118 load_status: LoadStatus,
119 status_text: Option<String>,
120 page_title: Option<String>,
121 favicon: Option<Image>,
122 focused: bool,
123 animating: bool,
124 cursor: Cursor,
125
126 back_forward_list: Vec<Url>,
128
129 back_forward_list_index: usize,
131}
132
133impl Drop for WebViewInner {
134 fn drop(&mut self) {
135 self.servo
136 .constellation_proxy()
137 .send(EmbedderToConstellationMessage::CloseWebView(self.id));
138 self.servo.paint_mut().remove_webview(self.id);
139 }
140}
141
142impl WebView {
143 pub(crate) fn new(mut builder: WebViewBuilder) -> Self {
144 let servo = builder.servo;
145 let painter_id = servo
146 .paint_mut()
147 .register_rendering_context(builder.rendering_context.clone());
148
149 let id = WebViewId::new(painter_id);
150 let webview = Self(Rc::new(RefCell::new(WebViewInner {
151 id,
152 servo: servo.clone(),
153 rendering_context: builder.rendering_context,
154 delegate: builder.delegate,
155 clipboard_delegate: builder
156 .clipboard_delegate
157 .unwrap_or_else(|| Rc::new(DefaultClipboardDelegate)),
158 #[cfg(feature = "gamepad")]
159 gamepad_delegate: builder
160 .gamepad_delegate
161 .unwrap_or_else(|| Rc::new(DefaultGamepadDelegate)),
162 accesskit_tree_id: None,
163 grafted_accesskit_tree_id: None,
164 grafted_accesskit_tree_epoch: None,
165 hidpi_scale_factor: builder.hidpi_scale_factor,
166 load_status: LoadStatus::Started,
167 status_text: None,
168 page_title: None,
169 favicon: None,
170 focused: false,
171 animating: false,
172 cursor: Cursor::Pointer,
173 back_forward_list: Default::default(),
174 back_forward_list_index: 0,
175 user_content_manager: builder.user_content_manager.clone(),
176 })));
177
178 let viewport_details = webview.viewport_details();
179 servo.paint().add_webview(
180 Box::new(ServoRendererWebView {
181 weak_handle: webview.weak_handle(),
182 id,
183 }),
184 viewport_details,
185 );
186
187 servo
188 .webviews_mut()
189 .insert(webview.id(), webview.weak_handle());
190
191 let user_content_manager_id = builder
192 .user_content_manager
193 .as_ref()
194 .map(|user_content_manager| user_content_manager.id());
195
196 let new_webview_details = NewWebViewDetails {
197 webview_id: webview.id(),
198 viewport_details,
199 user_content_manager_id,
200 };
201
202 match builder.create_new_webview_responder.as_mut() {
208 Some(responder) => {
209 let _ = responder.send(Some(new_webview_details));
210 },
211 None => {
212 let url = builder.url.unwrap_or(
213 Url::parse("about:blank")
214 .expect("Should always be able to parse 'about:blank'."),
215 );
216
217 servo
218 .constellation_proxy()
219 .send(EmbedderToConstellationMessage::NewWebView(
220 url.into(),
221 new_webview_details,
222 ));
223 },
224 }
225
226 webview
227 }
228
229 fn inner(&self) -> Ref<'_, WebViewInner> {
230 self.0.borrow()
231 }
232
233 fn inner_mut(&self) -> RefMut<'_, WebViewInner> {
234 self.0.borrow_mut()
235 }
236
237 pub(crate) fn request_create_new(
238 &self,
239 response_sender: GenericSender<Option<NewWebViewDetails>>,
240 ) {
241 let request = CreateNewWebViewRequest {
242 servo: self.inner().servo.clone(),
243 responder: IpcResponder::new(response_sender, None),
244 };
245 self.delegate().request_create_new(self.clone(), request);
246 }
247
248 pub(crate) fn viewport_details(&self) -> ViewportDetails {
249 let inner = self.inner();
252 let viewport_size = inner.rendering_context.size2d().to_f32();
253 let scaled_viewport_size = viewport_size / inner.hidpi_scale_factor;
254 let device_size = self
255 .delegate()
256 .screen_geometry(self.clone())
257 .map(|geometry| geometry.size.to_f32())
258 .unwrap_or_else(|| viewport_size);
259 ViewportDetails {
260 size: scaled_viewport_size / Scale::new(1.0),
261 hidpi_scale_factor: Scale::new(inner.hidpi_scale_factor.0),
262 device_size,
263 }
264 }
265
266 pub(crate) fn from_weak_handle(inner: &Weak<RefCell<WebViewInner>>) -> Option<Self> {
267 inner.upgrade().map(WebView)
268 }
269
270 pub(crate) fn weak_handle(&self) -> Weak<RefCell<WebViewInner>> {
271 Rc::downgrade(&self.0)
272 }
273
274 pub fn delegate(&self) -> Rc<dyn WebViewDelegate> {
276 self.inner().delegate.clone()
277 }
278
279 pub fn clipboard_delegate(&self) -> Rc<dyn ClipboardDelegate> {
281 self.inner().clipboard_delegate.clone()
282 }
283
284 #[cfg(feature = "gamepad")]
286 pub fn gamepad_delegate(&self) -> Rc<dyn GamepadDelegate> {
287 self.inner().gamepad_delegate.clone()
288 }
289
290 pub fn id(&self) -> WebViewId {
292 self.inner().id
293 }
294
295 pub fn rendering_context(&self) -> Rc<dyn RenderingContext> {
297 self.inner().rendering_context.clone()
298 }
299
300 pub fn load_status(&self) -> LoadStatus {
305 self.inner().load_status
306 }
307
308 pub(crate) fn set_load_status(self, new_value: LoadStatus) {
309 if self.inner().load_status == new_value {
310 return;
311 }
312 self.inner_mut().load_status = new_value;
313 self.delegate().notify_load_status_changed(self, new_value);
314 }
315
316 pub fn url(&self) -> Option<Url> {
319 let inner = self.inner();
320 inner
321 .back_forward_list
322 .get(inner.back_forward_list_index)
323 .cloned()
324 }
325
326 pub fn status_text(&self) -> Option<String> {
332 self.inner().status_text.clone()
333 }
334
335 pub(crate) fn set_status_text(self, new_value: Option<String>) {
336 if self.inner().status_text == new_value {
337 return;
338 }
339 self.inner_mut().status_text = new_value.clone();
340 self.delegate().notify_status_text_changed(self, new_value);
341 }
342
343 pub fn page_title(&self) -> Option<String> {
349 self.inner().page_title.clone()
350 }
351
352 pub(crate) fn set_page_title(self, new_value: Option<String>) {
353 if self.inner().page_title == new_value {
354 return;
355 }
356 self.inner_mut().page_title = new_value.clone();
357 self.delegate().notify_page_title_changed(self, new_value);
358 }
359
360 pub fn favicon(&self) -> Option<Ref<'_, Image>> {
367 Ref::filter_map(self.inner(), |inner| inner.favicon.as_ref()).ok()
368 }
369
370 pub(crate) fn set_favicon(self, new_value: Image) {
371 self.inner_mut().favicon = Some(new_value);
372 self.delegate().notify_favicon_changed(self);
373 }
374
375 pub fn focused(&self) -> bool {
380 self.inner().focused
381 }
382
383 pub(crate) fn set_focused(self, new_value: bool) {
384 if self.inner().focused == new_value {
385 return;
386 }
387 self.inner_mut().focused = new_value;
388 self.delegate().notify_focus_changed(self, new_value);
389 }
390
391 pub fn cursor(&self) -> Cursor {
397 self.inner().cursor
398 }
399
400 pub(crate) fn set_cursor(self, new_value: Cursor) {
401 if self.inner().cursor == new_value {
402 return;
403 }
404 self.inner_mut().cursor = new_value;
405 self.delegate().notify_cursor_changed(self, new_value);
406 }
407
408 pub fn focus(&self) {
410 self.inner()
411 .servo
412 .constellation_proxy()
413 .send(EmbedderToConstellationMessage::FocusWebView(self.id()));
414 }
415
416 pub fn blur(&self) {
418 self.inner()
419 .servo
420 .constellation_proxy()
421 .send(EmbedderToConstellationMessage::BlurWebView);
422 }
423
424 pub fn animating(&self) -> bool {
429 self.inner().animating
430 }
431
432 pub(crate) fn set_animating(self, new_value: bool) {
433 if self.inner().animating == new_value {
434 return;
435 }
436 self.inner_mut().animating = new_value;
437 self.delegate().notify_animating_changed(self, new_value);
438 }
439
440 pub fn size(&self) -> DeviceSize {
442 self.inner().rendering_context.size2d().to_f32()
443 }
444
445 pub fn resize(&self, new_size: PhysicalSize<u32>) {
452 let new_size = PhysicalSize {
453 width: new_size.width.max(MINIMUM_WEBVIEW_SIZE.width as u32),
454 height: new_size.height.max(MINIMUM_WEBVIEW_SIZE.height as u32),
455 };
456
457 self.inner()
458 .servo
459 .paint()
460 .resize_rendering_context(self.id(), new_size);
461 }
462
463 pub fn hidpi_scale_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel> {
465 self.inner().hidpi_scale_factor
466 }
467
468 pub fn set_hidpi_scale_factor(
473 &self,
474 new_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
475 ) {
476 if self.inner().hidpi_scale_factor == new_scale_factor {
477 return;
478 }
479
480 self.inner_mut().hidpi_scale_factor = new_scale_factor;
481 self.inner()
482 .servo
483 .paint()
484 .set_hidpi_scale_factor(self.id(), new_scale_factor);
485 }
486
487 pub fn show(&self) {
489 self.inner()
490 .servo
491 .paint()
492 .show_webview(self.id())
493 .expect("BUG: invalid WebView instance");
494 }
495
496 pub fn hide(&self) {
498 self.inner()
499 .servo
500 .paint()
501 .hide_webview(self.id())
502 .expect("BUG: invalid WebView instance");
503 }
504
505 pub fn notify_theme_change(&self, theme: Theme) {
507 self.inner()
508 .servo
509 .constellation_proxy()
510 .send(EmbedderToConstellationMessage::ThemeChange(
511 self.id(),
512 theme,
513 ))
514 }
515
516 pub fn load(&self, url: Url) {
521 self.inner()
522 .servo
523 .constellation_proxy()
524 .send(EmbedderToConstellationMessage::LoadUrl(
525 self.id(),
526 UrlRequest::new(url),
527 ))
528 }
529
530 pub fn load_request(&self, url_request: UrlRequest) {
535 self.inner()
536 .servo
537 .constellation_proxy()
538 .send(EmbedderToConstellationMessage::LoadUrl(
539 self.id(),
540 url_request,
541 ))
542 }
543
544 pub fn reload(&self) {
546 self.inner_mut().load_status = LoadStatus::Started;
547 self.inner()
548 .servo
549 .constellation_proxy()
550 .send(EmbedderToConstellationMessage::Reload(self.id()))
551 }
552
553 pub fn can_go_back(&self) -> bool {
558 self.inner().back_forward_list_index != 0
559 }
560
561 pub fn go_back(&self, amount: usize) -> TraversalId {
567 let traversal_id = TraversalId::new();
568 self.inner().servo.constellation_proxy().send(
569 EmbedderToConstellationMessage::TraverseHistory(
570 self.id(),
571 TraversalDirection::Back(amount),
572 traversal_id.clone(),
573 ),
574 );
575 traversal_id
576 }
577
578 pub fn can_go_forward(&self) -> bool {
583 let inner = self.inner();
584 inner.back_forward_list.len() > inner.back_forward_list_index + 1
585 }
586
587 pub fn go_forward(&self, amount: usize) -> TraversalId {
593 let traversal_id = TraversalId::new();
594 self.inner().servo.constellation_proxy().send(
595 EmbedderToConstellationMessage::TraverseHistory(
596 self.id(),
597 TraversalDirection::Forward(amount),
598 traversal_id.clone(),
599 ),
600 );
601 traversal_id
602 }
603
604 pub fn notify_scroll_event(&self, scroll: Scroll, point: WebViewPoint) {
607 self.inner()
608 .servo
609 .paint()
610 .notify_scroll_event(self.id(), scroll, point);
611 }
612
613 pub fn notify_input_event(&self, event: InputEvent) -> InputEventId {
620 let event: InputEventAndId = event.into();
621 let event_id = event.id;
622 let webview_id = self.id();
623 let servo = &self.inner().servo;
624 if event.event.point().is_some() {
626 if !servo.paint().notify_input_event(self.id(), event) {
627 servo.add_pending_handled_input_event(PendingHandledInputEvent {
628 event_id,
629 webview_id,
630 });
631 servo.event_loop_waker().wake();
632 }
633 } else {
634 servo
635 .constellation_proxy()
636 .send(EmbedderToConstellationMessage::ForwardInputEvent(
637 webview_id, event, None, ));
639 }
640
641 event_id
642 }
643
644 pub fn notify_media_session_action_event(&self, event: MediaSessionActionType) {
646 self.inner()
647 .servo
648 .constellation_proxy()
649 .send(EmbedderToConstellationMessage::MediaSessionAction(event));
650 }
651
652 pub fn set_page_zoom(&self, new_zoom: f32) {
662 self.inner()
663 .servo
664 .paint()
665 .set_page_zoom(self.id(), new_zoom);
666 }
667
668 pub fn page_zoom(&self) -> f32 {
670 self.inner().servo.paint().page_zoom(self.id())
671 }
672
673 pub fn adjust_pinch_zoom(&self, pinch_zoom_delta: f32, center: DevicePoint) {
684 self.inner()
685 .servo
686 .paint()
687 .adjust_pinch_zoom(self.id(), pinch_zoom_delta, center);
688 }
689
690 pub fn pinch_zoom(&self) -> f32 {
692 self.inner().servo.paint().pinch_zoom(self.id())
693 }
694
695 pub fn device_pixels_per_css_pixel(&self) -> Scale<f32, CSSPixel, DevicePixel> {
700 self.inner()
701 .servo
702 .paint()
703 .device_pixels_per_page_pixel(self.id())
704 }
705
706 pub fn exit_fullscreen(&self) {
708 self.inner()
709 .servo
710 .constellation_proxy()
711 .send(EmbedderToConstellationMessage::ExitFullScreen(self.id()));
712 }
713
714 pub fn set_throttled(&self, throttled: bool) {
719 self.inner().servo.constellation_proxy().send(
720 EmbedderToConstellationMessage::SetWebViewThrottled(self.id(), throttled),
721 );
722 }
723
724 pub fn toggle_webrender_debugging(&self, debugging: WebRenderDebugOption) {
730 self.inner().servo.paint().toggle_webrender_debug(debugging);
731 }
732
733 pub fn capture_webrender(&self) {
738 self.inner().servo.paint().capture_webrender(self.id());
739 }
740
741 pub fn toggle_sampling_profiler(&self, rate: Duration, max_duration: Duration) {
749 self.inner().servo.constellation_proxy().send(
750 EmbedderToConstellationMessage::ToggleProfiler(rate, max_duration),
751 );
752 }
753
754 pub fn paint(&self) {
756 self.inner().servo.paint().render(self.id());
757 }
758
759 pub fn user_content_manager(&self) -> Option<Rc<UserContentManager>> {
761 self.inner().user_content_manager.clone()
762 }
763
764 pub fn evaluate_javascript<T: ToString>(
767 &self,
768 script: T,
769 callback: impl FnOnce(Result<JSValue, JavaScriptEvaluationError>) + 'static,
770 ) {
771 self.inner().servo.javascript_evaluator_mut().evaluate(
772 self.id(),
773 script.to_string(),
774 Box::new(callback),
775 );
776 }
777
778 pub fn take_screenshot(
796 &self,
797 rect: Option<WebViewRect>,
798 callback: impl FnOnce(Result<RgbaImage, ScreenshotCaptureError>) + 'static,
799 ) {
800 self.inner()
801 .servo
802 .paint()
803 .request_screenshot(self.id(), rect, Box::new(callback));
804 }
805
806 pub(crate) fn set_history(self, new_back_forward_list: Vec<ServoUrl>, new_index: usize) {
807 {
808 let mut inner_mut = self.inner_mut();
809 inner_mut.back_forward_list_index = new_index;
810 inner_mut.back_forward_list = new_back_forward_list
811 .into_iter()
812 .map(ServoUrl::into_url)
813 .collect();
814 }
815
816 let back_forward_list = self.inner().back_forward_list.clone();
817 let back_forward_list_index = self.inner().back_forward_list_index;
818 self.delegate().notify_url_changed(
819 self.clone(),
820 back_forward_list[back_forward_list_index].clone(),
821 );
822 self.delegate().notify_history_changed(
823 self.clone(),
824 back_forward_list,
825 back_forward_list_index,
826 );
827 }
828
829 pub(crate) fn show_embedder_control(
830 self,
831 control_id: EmbedderControlId,
832 position: DeviceIntRect,
833 embedder_control_request: EmbedderControlRequest,
834 ) {
835 let constellation_proxy = self.inner().servo.constellation_proxy().clone();
836 let embedder_control = match embedder_control_request {
837 EmbedderControlRequest::SelectElement(request) => {
838 EmbedderControl::SelectElement(SelectElement {
839 id: control_id,
840 select_element_request: request,
841 position,
842 constellation_proxy,
843 response_sent: false,
844 })
845 },
846 EmbedderControlRequest::ColorPicker(current_color) => {
847 EmbedderControl::ColorPicker(ColorPicker {
848 id: control_id,
849 current_color: Some(current_color),
850 position,
851 constellation_proxy,
852 response_sent: false,
853 })
854 },
855 EmbedderControlRequest::InputMethod(input_method_request) => {
856 EmbedderControl::InputMethod(InputMethodControl {
857 id: control_id,
858 input_method_type: input_method_request.input_method_type,
859 text: input_method_request.text,
860 insertion_point: input_method_request.insertion_point,
861 position,
862 multiline: input_method_request.multiline,
863 allow_virtual_keyboard: input_method_request.allow_virtual_keyboard,
864 })
865 },
866 EmbedderControlRequest::ContextMenu(mut context_menu_request) => {
867 for item in context_menu_request.items.iter_mut() {
868 match item {
869 ContextMenuItem::Item {
870 action: ContextMenuAction::GoBack,
871 enabled,
872 ..
873 } => *enabled = self.can_go_back(),
874 ContextMenuItem::Item {
875 action: ContextMenuAction::GoForward,
876 enabled,
877 ..
878 } => *enabled = self.can_go_forward(),
879 _ => {},
880 }
881 }
882 EmbedderControl::ContextMenu(ContextMenu {
883 id: control_id,
884 position,
885 items: context_menu_request.items,
886 element_info: context_menu_request.element_info,
887 constellation_proxy,
888 response_sent: false,
889 })
890 },
891 EmbedderControlRequest::FilePicker { .. } => {
892 unreachable!("This message should be routed through the FileManagerThread")
893 },
894 };
895
896 self.delegate()
897 .show_embedder_control(self.clone(), embedder_control);
898 }
899
900 pub fn accesskit_tree_id(&self) -> Option<TreeId> {
902 self.inner().accesskit_tree_id
903 }
904
905 pub fn set_accessibility_active(&self, active: bool) -> Option<TreeId> {
920 if !pref!(accessibility_enabled) {
921 return None;
922 }
923
924 if active == self.inner().accesskit_tree_id.is_some() {
925 return self.accesskit_tree_id();
926 }
927
928 if active {
929 let accesskit_tree_id = TreeId(AccesskitUuid::new_v4());
930 self.inner_mut().accesskit_tree_id = Some(accesskit_tree_id);
931 } else {
932 self.inner_mut().accesskit_tree_id = None;
933 self.inner_mut().grafted_accesskit_tree_id = None;
934 self.inner_mut().grafted_accesskit_tree_epoch = None;
935 }
936
937 self.inner().servo.constellation_proxy().send(
938 EmbedderToConstellationMessage::SetAccessibilityActive(self.id(), active),
939 );
940
941 self.accesskit_tree_id()
942 }
943
944 pub(crate) fn notify_document_accessibility_tree_id(&self, grafted_tree_id: TreeId) {
945 let Some(webview_accesskit_tree_id) = self.inner().accesskit_tree_id else {
946 return;
947 };
948 let old_grafted_tree_id = self
949 .inner_mut()
950 .grafted_accesskit_tree_id
951 .replace(grafted_tree_id);
952 if old_grafted_tree_id == Some(grafted_tree_id) {
955 return;
956 }
957 let root_node_id = NodeId(0);
958 let mut root_node = AccesskitNode::new(Role::ScrollView);
959 let graft_node_id = NodeId(1);
960 let mut graft_node = AccesskitNode::new(Role::GenericContainer);
961 graft_node.set_tree_id(grafted_tree_id);
962 root_node.set_children(vec![graft_node_id]);
963 self.delegate().notify_accessibility_tree_update(
964 self.clone(),
965 TreeUpdate {
966 nodes: vec![(root_node_id, root_node), (graft_node_id, graft_node)],
967 tree: Some(Tree {
968 root: root_node_id,
969 toolkit_name: None,
970 toolkit_version: None,
971 }),
972 tree_id: webview_accesskit_tree_id,
973 focus: root_node_id,
974 },
975 );
976 }
977
978 pub(crate) fn process_accessibility_tree_update(&self, tree_update: TreeUpdate, epoch: Epoch) {
979 if self
980 .inner()
981 .grafted_accesskit_tree_epoch
982 .is_some_and(|current| epoch < current)
983 {
984 debug!("Ignoring stale tree update for {:?}", tree_update.tree_id);
988 return;
989 }
990 if self
991 .inner()
992 .grafted_accesskit_tree_epoch
993 .is_none_or(|current| epoch > current)
994 {
995 self.notify_document_accessibility_tree_id(tree_update.tree_id);
996 self.inner_mut().grafted_accesskit_tree_epoch = Some(epoch);
997 }
998 self.delegate()
999 .notify_accessibility_tree_update(self.clone(), tree_update);
1000 }
1001}
1002
1003struct ServoRendererWebView {
1006 id: WebViewId,
1007 weak_handle: Weak<RefCell<WebViewInner>>,
1008}
1009
1010impl WebViewTrait for ServoRendererWebView {
1011 fn id(&self) -> WebViewId {
1012 self.id
1013 }
1014
1015 fn screen_geometry(&self) -> Option<ScreenGeometry> {
1016 let webview = WebView::from_weak_handle(&self.weak_handle)?;
1017 webview.delegate().screen_geometry(webview)
1018 }
1019
1020 fn set_animating(&self, new_value: bool) {
1021 if let Some(webview) = WebView::from_weak_handle(&self.weak_handle) {
1022 webview.set_animating(new_value);
1023 }
1024 }
1025}
1026
1027pub struct WebViewBuilder {
1029 servo: Servo,
1030 rendering_context: Rc<dyn RenderingContext>,
1031 delegate: Rc<dyn WebViewDelegate>,
1032 url: Option<Url>,
1033 hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
1034 create_new_webview_responder: Option<IpcResponder<Option<NewWebViewDetails>>>,
1035 user_content_manager: Option<Rc<UserContentManager>>,
1036 clipboard_delegate: Option<Rc<dyn ClipboardDelegate>>,
1037 #[cfg(feature = "gamepad")]
1038 gamepad_delegate: Option<Rc<dyn GamepadDelegate>>,
1039}
1040
1041impl WebViewBuilder {
1042 pub fn new(servo: &Servo, rendering_context: Rc<dyn RenderingContext>) -> Self {
1047 Self {
1048 servo: servo.clone(),
1049 rendering_context,
1050 url: None,
1051 hidpi_scale_factor: Scale::new(1.0),
1052 delegate: Rc::new(DefaultWebViewDelegate),
1053 create_new_webview_responder: None,
1054 user_content_manager: None,
1055 clipboard_delegate: None,
1056 #[cfg(feature = "gamepad")]
1057 gamepad_delegate: None,
1058 }
1059 }
1060
1061 pub(crate) fn new_for_create_request(
1062 servo: &Servo,
1063 rendering_context: Rc<dyn RenderingContext>,
1064 responder: IpcResponder<Option<NewWebViewDetails>>,
1065 ) -> Self {
1066 let mut builder = Self::new(servo, rendering_context);
1067 builder.create_new_webview_responder = Some(responder);
1068 builder
1069 }
1070
1071 pub fn delegate(mut self, delegate: Rc<dyn WebViewDelegate>) -> Self {
1074 self.delegate = delegate;
1075 self
1076 }
1077
1078 pub fn url(mut self, url: Url) -> Self {
1080 self.url = Some(url);
1081 self
1082 }
1083
1084 pub fn hidpi_scale_factor(
1086 mut self,
1087 hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
1088 ) -> Self {
1089 self.hidpi_scale_factor = hidpi_scale_factor;
1090 self
1091 }
1092
1093 pub fn user_content_manager(mut self, user_content_manager: Rc<UserContentManager>) -> Self {
1097 self.user_content_manager = Some(user_content_manager);
1098 self
1099 }
1100
1101 pub fn clipboard_delegate(mut self, clipboard_delegate: Rc<dyn ClipboardDelegate>) -> Self {
1104 self.clipboard_delegate = Some(clipboard_delegate);
1105 self
1106 }
1107
1108 #[cfg(feature = "gamepad")]
1111 pub fn gamepad_delegate(mut self, gamepad_delegate: Rc<dyn GamepadDelegate>) -> Self {
1112 self.gamepad_delegate = Some(gamepad_delegate);
1113 self
1114 }
1115
1116 pub fn build(self) -> WebView {
1118 WebView::new(self)
1119 }
1120}