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 scaled_viewport_size =
253 inner.rendering_context.size2d().to_f32() / inner.hidpi_scale_factor;
254 ViewportDetails {
255 size: scaled_viewport_size / Scale::new(1.0),
256 hidpi_scale_factor: Scale::new(inner.hidpi_scale_factor.0),
257 }
258 }
259
260 pub(crate) fn from_weak_handle(inner: &Weak<RefCell<WebViewInner>>) -> Option<Self> {
261 inner.upgrade().map(WebView)
262 }
263
264 pub(crate) fn weak_handle(&self) -> Weak<RefCell<WebViewInner>> {
265 Rc::downgrade(&self.0)
266 }
267
268 pub fn delegate(&self) -> Rc<dyn WebViewDelegate> {
269 self.inner().delegate.clone()
270 }
271
272 pub fn clipboard_delegate(&self) -> Rc<dyn ClipboardDelegate> {
273 self.inner().clipboard_delegate.clone()
274 }
275
276 #[cfg(feature = "gamepad")]
277 pub fn gamepad_delegate(&self) -> Rc<dyn GamepadDelegate> {
278 self.inner().gamepad_delegate.clone()
279 }
280
281 pub fn id(&self) -> WebViewId {
282 self.inner().id
283 }
284
285 pub fn load_status(&self) -> LoadStatus {
286 self.inner().load_status
287 }
288
289 pub(crate) fn set_load_status(self, new_value: LoadStatus) {
290 if self.inner().load_status == new_value {
291 return;
292 }
293 self.inner_mut().load_status = new_value;
294 self.delegate().notify_load_status_changed(self, new_value);
295 }
296
297 pub fn url(&self) -> Option<Url> {
298 let inner = self.inner();
299 inner
300 .back_forward_list
301 .get(inner.back_forward_list_index)
302 .cloned()
303 }
304
305 pub fn status_text(&self) -> Option<String> {
306 self.inner().status_text.clone()
307 }
308
309 pub(crate) fn set_status_text(self, new_value: Option<String>) {
310 if self.inner().status_text == new_value {
311 return;
312 }
313 self.inner_mut().status_text = new_value.clone();
314 self.delegate().notify_status_text_changed(self, new_value);
315 }
316
317 pub fn page_title(&self) -> Option<String> {
318 self.inner().page_title.clone()
319 }
320
321 pub(crate) fn set_page_title(self, new_value: Option<String>) {
322 if self.inner().page_title == new_value {
323 return;
324 }
325 self.inner_mut().page_title = new_value.clone();
326 self.delegate().notify_page_title_changed(self, new_value);
327 }
328
329 pub fn favicon(&self) -> Option<Ref<'_, Image>> {
330 Ref::filter_map(self.inner(), |inner| inner.favicon.as_ref()).ok()
331 }
332
333 pub(crate) fn set_favicon(self, new_value: Image) {
334 self.inner_mut().favicon = Some(new_value);
335 self.delegate().notify_favicon_changed(self);
336 }
337
338 pub fn focused(&self) -> bool {
339 self.inner().focused
340 }
341
342 pub(crate) fn set_focused(self, new_value: bool) {
343 if self.inner().focused == new_value {
344 return;
345 }
346 self.inner_mut().focused = new_value;
347 self.delegate().notify_focus_changed(self, new_value);
348 }
349
350 pub fn cursor(&self) -> Cursor {
351 self.inner().cursor
352 }
353
354 pub(crate) fn set_cursor(self, new_value: Cursor) {
355 if self.inner().cursor == new_value {
356 return;
357 }
358 self.inner_mut().cursor = new_value;
359 self.delegate().notify_cursor_changed(self, new_value);
360 }
361
362 pub fn focus(&self) {
363 self.inner()
364 .servo
365 .constellation_proxy()
366 .send(EmbedderToConstellationMessage::FocusWebView(self.id()));
367 }
368
369 pub fn blur(&self) {
370 self.inner()
371 .servo
372 .constellation_proxy()
373 .send(EmbedderToConstellationMessage::BlurWebView);
374 }
375
376 pub fn animating(&self) -> bool {
381 self.inner().animating
382 }
383
384 pub(crate) fn set_animating(self, new_value: bool) {
385 if self.inner().animating == new_value {
386 return;
387 }
388 self.inner_mut().animating = new_value;
389 self.delegate().notify_animating_changed(self, new_value);
390 }
391
392 pub fn size(&self) -> DeviceSize {
394 self.inner().rendering_context.size2d().to_f32()
395 }
396
397 pub fn resize(&self, new_size: PhysicalSize<u32>) {
404 let new_size = PhysicalSize {
405 width: new_size.width.max(MINIMUM_WEBVIEW_SIZE.width as u32),
406 height: new_size.height.max(MINIMUM_WEBVIEW_SIZE.height as u32),
407 };
408
409 self.inner()
410 .servo
411 .paint()
412 .resize_rendering_context(self.id(), new_size);
413 }
414
415 pub fn hidpi_scale_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel> {
416 self.inner().hidpi_scale_factor
417 }
418
419 pub fn set_hidpi_scale_factor(
420 &self,
421 new_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
422 ) {
423 if self.inner().hidpi_scale_factor == new_scale_factor {
424 return;
425 }
426
427 self.inner_mut().hidpi_scale_factor = new_scale_factor;
428 self.inner()
429 .servo
430 .paint()
431 .set_hidpi_scale_factor(self.id(), new_scale_factor);
432 }
433
434 pub fn show(&self) {
435 self.inner()
436 .servo
437 .paint()
438 .show_webview(self.id())
439 .expect("BUG: invalid WebView instance");
440 }
441
442 pub fn hide(&self) {
443 self.inner()
444 .servo
445 .paint()
446 .hide_webview(self.id())
447 .expect("BUG: invalid WebView instance");
448 }
449
450 pub fn notify_theme_change(&self, theme: Theme) {
451 self.inner()
452 .servo
453 .constellation_proxy()
454 .send(EmbedderToConstellationMessage::ThemeChange(
455 self.id(),
456 theme,
457 ))
458 }
459
460 pub fn load(&self, url: Url) {
461 self.inner()
462 .servo
463 .constellation_proxy()
464 .send(EmbedderToConstellationMessage::LoadUrl(
465 self.id(),
466 UrlRequest::new(url),
467 ))
468 }
469
470 pub fn load_request(&self, url_request: UrlRequest) {
472 self.inner()
473 .servo
474 .constellation_proxy()
475 .send(EmbedderToConstellationMessage::LoadUrl(
476 self.id(),
477 url_request,
478 ))
479 }
480
481 pub fn reload(&self) {
482 self.inner_mut().load_status = LoadStatus::Started;
483 self.inner()
484 .servo
485 .constellation_proxy()
486 .send(EmbedderToConstellationMessage::Reload(self.id()))
487 }
488
489 pub fn can_go_back(&self) -> bool {
490 self.inner().back_forward_list_index != 0
491 }
492
493 pub fn go_back(&self, amount: usize) -> TraversalId {
494 let traversal_id = TraversalId::new();
495 self.inner().servo.constellation_proxy().send(
496 EmbedderToConstellationMessage::TraverseHistory(
497 self.id(),
498 TraversalDirection::Back(amount),
499 traversal_id.clone(),
500 ),
501 );
502 traversal_id
503 }
504
505 pub fn can_go_forward(&self) -> bool {
506 let inner = self.inner();
507 inner.back_forward_list.len() > inner.back_forward_list_index + 1
508 }
509
510 pub fn go_forward(&self, amount: usize) -> TraversalId {
511 let traversal_id = TraversalId::new();
512 self.inner().servo.constellation_proxy().send(
513 EmbedderToConstellationMessage::TraverseHistory(
514 self.id(),
515 TraversalDirection::Forward(amount),
516 traversal_id.clone(),
517 ),
518 );
519 traversal_id
520 }
521
522 pub fn notify_scroll_event(&self, scroll: Scroll, point: WebViewPoint) {
525 self.inner()
526 .servo
527 .paint()
528 .notify_scroll_event(self.id(), scroll, point);
529 }
530
531 pub fn notify_input_event(&self, event: InputEvent) -> InputEventId {
532 let event: InputEventAndId = event.into();
533 let event_id = event.id;
534 let webview_id = self.id();
535 let servo = &self.inner().servo;
536 if event.event.point().is_some() {
538 if !servo.paint().notify_input_event(self.id(), event) {
539 servo.add_pending_handled_input_event(PendingHandledInputEvent {
540 event_id,
541 webview_id,
542 });
543 servo.event_loop_waker().wake();
544 }
545 } else {
546 servo
547 .constellation_proxy()
548 .send(EmbedderToConstellationMessage::ForwardInputEvent(
549 webview_id, event, None, ));
551 }
552
553 event_id
554 }
555
556 pub fn notify_media_session_action_event(&self, event: MediaSessionActionType) {
557 self.inner()
558 .servo
559 .constellation_proxy()
560 .send(EmbedderToConstellationMessage::MediaSessionAction(event));
561 }
562
563 pub fn set_page_zoom(&self, new_zoom: f32) {
573 self.inner()
574 .servo
575 .paint()
576 .set_page_zoom(self.id(), new_zoom);
577 }
578
579 pub fn page_zoom(&self) -> f32 {
581 self.inner().servo.paint().page_zoom(self.id())
582 }
583
584 pub fn adjust_pinch_zoom(&self, pinch_zoom_delta: f32, center: DevicePoint) {
595 self.inner()
596 .servo
597 .paint()
598 .adjust_pinch_zoom(self.id(), pinch_zoom_delta, center);
599 }
600
601 pub fn pinch_zoom(&self) -> f32 {
603 self.inner().servo.paint().pinch_zoom(self.id())
604 }
605
606 pub fn device_pixels_per_css_pixel(&self) -> Scale<f32, CSSPixel, DevicePixel> {
607 self.inner()
608 .servo
609 .paint()
610 .device_pixels_per_page_pixel(self.id())
611 }
612
613 pub fn exit_fullscreen(&self) {
614 self.inner()
615 .servo
616 .constellation_proxy()
617 .send(EmbedderToConstellationMessage::ExitFullScreen(self.id()));
618 }
619
620 pub fn set_throttled(&self, throttled: bool) {
621 self.inner().servo.constellation_proxy().send(
622 EmbedderToConstellationMessage::SetWebViewThrottled(self.id(), throttled),
623 );
624 }
625
626 pub fn toggle_webrender_debugging(&self, debugging: WebRenderDebugOption) {
627 self.inner().servo.paint().toggle_webrender_debug(debugging);
628 }
629
630 pub fn capture_webrender(&self) {
631 self.inner().servo.paint().capture_webrender(self.id());
632 }
633
634 pub fn toggle_sampling_profiler(&self, rate: Duration, max_duration: Duration) {
635 self.inner().servo.constellation_proxy().send(
636 EmbedderToConstellationMessage::ToggleProfiler(rate, max_duration),
637 );
638 }
639
640 pub fn send_error(&self, message: String) {
641 self.inner()
642 .servo
643 .constellation_proxy()
644 .send(EmbedderToConstellationMessage::SendError(
645 Some(self.id()),
646 message,
647 ));
648 }
649
650 pub fn paint(&self) {
652 self.inner().servo.paint().render(self.id());
653 }
654
655 pub fn user_content_manager(&self) -> Option<Rc<UserContentManager>> {
657 self.inner().user_content_manager.clone()
658 }
659
660 pub fn evaluate_javascript<T: ToString>(
663 &self,
664 script: T,
665 callback: impl FnOnce(Result<JSValue, JavaScriptEvaluationError>) + 'static,
666 ) {
667 self.inner().servo.javascript_evaluator_mut().evaluate(
668 self.id(),
669 script.to_string(),
670 Box::new(callback),
671 );
672 }
673
674 pub fn take_screenshot(
692 &self,
693 rect: Option<WebViewRect>,
694 callback: impl FnOnce(Result<RgbaImage, ScreenshotCaptureError>) + 'static,
695 ) {
696 self.inner()
697 .servo
698 .paint()
699 .request_screenshot(self.id(), rect, Box::new(callback));
700 }
701
702 pub(crate) fn set_history(self, new_back_forward_list: Vec<ServoUrl>, new_index: usize) {
703 {
704 let mut inner_mut = self.inner_mut();
705 inner_mut.back_forward_list_index = new_index;
706 inner_mut.back_forward_list = new_back_forward_list
707 .into_iter()
708 .map(ServoUrl::into_url)
709 .collect();
710 }
711
712 let back_forward_list = self.inner().back_forward_list.clone();
713 let back_forward_list_index = self.inner().back_forward_list_index;
714 self.delegate().notify_url_changed(
715 self.clone(),
716 back_forward_list[back_forward_list_index].clone(),
717 );
718 self.delegate().notify_history_changed(
719 self.clone(),
720 back_forward_list,
721 back_forward_list_index,
722 );
723 }
724
725 pub(crate) fn show_embedder_control(
726 self,
727 control_id: EmbedderControlId,
728 position: DeviceIntRect,
729 embedder_control_request: EmbedderControlRequest,
730 ) {
731 let constellation_proxy = self.inner().servo.constellation_proxy().clone();
732 let embedder_control = match embedder_control_request {
733 EmbedderControlRequest::SelectElement(request) => {
734 EmbedderControl::SelectElement(SelectElement {
735 id: control_id,
736 select_element_request: request,
737 position,
738 constellation_proxy,
739 response_sent: false,
740 })
741 },
742 EmbedderControlRequest::ColorPicker(current_color) => {
743 EmbedderControl::ColorPicker(ColorPicker {
744 id: control_id,
745 current_color: Some(current_color),
746 position,
747 constellation_proxy,
748 response_sent: false,
749 })
750 },
751 EmbedderControlRequest::InputMethod(input_method_request) => {
752 EmbedderControl::InputMethod(InputMethodControl {
753 id: control_id,
754 input_method_type: input_method_request.input_method_type,
755 text: input_method_request.text,
756 insertion_point: input_method_request.insertion_point,
757 position,
758 multiline: input_method_request.multiline,
759 allow_virtual_keyboard: input_method_request.allow_virtual_keyboard,
760 })
761 },
762 EmbedderControlRequest::ContextMenu(mut context_menu_request) => {
763 for item in context_menu_request.items.iter_mut() {
764 match item {
765 ContextMenuItem::Item {
766 action: ContextMenuAction::GoBack,
767 enabled,
768 ..
769 } => *enabled = self.can_go_back(),
770 ContextMenuItem::Item {
771 action: ContextMenuAction::GoForward,
772 enabled,
773 ..
774 } => *enabled = self.can_go_forward(),
775 _ => {},
776 }
777 }
778 EmbedderControl::ContextMenu(ContextMenu {
779 id: control_id,
780 position,
781 items: context_menu_request.items,
782 element_info: context_menu_request.element_info,
783 constellation_proxy,
784 response_sent: false,
785 })
786 },
787 EmbedderControlRequest::FilePicker { .. } => {
788 unreachable!("This message should be routed through the FileManagerThread")
789 },
790 };
791
792 self.delegate()
793 .show_embedder_control(self.clone(), embedder_control);
794 }
795
796 pub fn accesskit_tree_id(&self) -> Option<TreeId> {
798 self.inner().accesskit_tree_id
799 }
800
801 pub fn set_accessibility_active(&self, active: bool) -> Option<TreeId> {
816 if !pref!(accessibility_enabled) {
817 return None;
818 }
819
820 if active == self.inner().accesskit_tree_id.is_some() {
821 return self.accesskit_tree_id();
822 }
823
824 if active {
825 let accesskit_tree_id = TreeId(AccesskitUuid::new_v4());
826 self.inner_mut().accesskit_tree_id = Some(accesskit_tree_id);
827 } else {
828 self.inner_mut().accesskit_tree_id = None;
829 self.inner_mut().grafted_accesskit_tree_id = None;
830 self.inner_mut().grafted_accesskit_tree_epoch = None;
831 }
832
833 self.inner().servo.constellation_proxy().send(
834 EmbedderToConstellationMessage::SetAccessibilityActive(self.id(), active),
835 );
836
837 self.accesskit_tree_id()
838 }
839
840 pub(crate) fn notify_document_accessibility_tree_id(&self, grafted_tree_id: TreeId) {
841 let Some(webview_accesskit_tree_id) = self.inner().accesskit_tree_id else {
842 return;
843 };
844 let old_grafted_tree_id = self
845 .inner_mut()
846 .grafted_accesskit_tree_id
847 .replace(grafted_tree_id);
848 if old_grafted_tree_id == Some(grafted_tree_id) {
851 return;
852 }
853 let root_node_id = NodeId(0);
854 let mut root_node = AccesskitNode::new(Role::ScrollView);
855 let graft_node_id = NodeId(1);
856 let mut graft_node = AccesskitNode::new(Role::GenericContainer);
857 graft_node.set_tree_id(grafted_tree_id);
858 root_node.set_children(vec![graft_node_id]);
859 self.delegate().notify_accessibility_tree_update(
860 self.clone(),
861 TreeUpdate {
862 nodes: vec![(root_node_id, root_node), (graft_node_id, graft_node)],
863 tree: Some(Tree {
864 root: root_node_id,
865 toolkit_name: None,
866 toolkit_version: None,
867 }),
868 tree_id: webview_accesskit_tree_id,
869 focus: root_node_id,
870 },
871 );
872 }
873
874 pub(crate) fn process_accessibility_tree_update(&self, tree_update: TreeUpdate, epoch: Epoch) {
875 if self
876 .inner()
877 .grafted_accesskit_tree_epoch
878 .is_some_and(|current| epoch < current)
879 {
880 debug!("Ignoring stale tree update for {:?}", tree_update.tree_id);
884 return;
885 }
886 if self
887 .inner()
888 .grafted_accesskit_tree_epoch
889 .is_none_or(|current| epoch > current)
890 {
891 self.notify_document_accessibility_tree_id(tree_update.tree_id);
892 self.inner_mut().grafted_accesskit_tree_epoch = Some(epoch);
893 }
894 self.delegate()
895 .notify_accessibility_tree_update(self.clone(), tree_update);
896 }
897}
898
899struct ServoRendererWebView {
902 id: WebViewId,
903 weak_handle: Weak<RefCell<WebViewInner>>,
904}
905
906impl WebViewTrait for ServoRendererWebView {
907 fn id(&self) -> WebViewId {
908 self.id
909 }
910
911 fn screen_geometry(&self) -> Option<ScreenGeometry> {
912 let webview = WebView::from_weak_handle(&self.weak_handle)?;
913 webview.delegate().screen_geometry(webview)
914 }
915
916 fn set_animating(&self, new_value: bool) {
917 if let Some(webview) = WebView::from_weak_handle(&self.weak_handle) {
918 webview.set_animating(new_value);
919 }
920 }
921}
922
923pub struct WebViewBuilder {
925 servo: Servo,
926 rendering_context: Rc<dyn RenderingContext>,
927 delegate: Rc<dyn WebViewDelegate>,
928 url: Option<Url>,
929 hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
930 create_new_webview_responder: Option<IpcResponder<Option<NewWebViewDetails>>>,
931 user_content_manager: Option<Rc<UserContentManager>>,
932 clipboard_delegate: Option<Rc<dyn ClipboardDelegate>>,
933 #[cfg(feature = "gamepad")]
934 gamepad_delegate: Option<Rc<dyn GamepadDelegate>>,
935}
936
937impl WebViewBuilder {
938 pub fn new(servo: &Servo, rendering_context: Rc<dyn RenderingContext>) -> Self {
939 Self {
940 servo: servo.clone(),
941 rendering_context,
942 url: None,
943 hidpi_scale_factor: Scale::new(1.0),
944 delegate: Rc::new(DefaultWebViewDelegate),
945 create_new_webview_responder: None,
946 user_content_manager: None,
947 clipboard_delegate: None,
948 #[cfg(feature = "gamepad")]
949 gamepad_delegate: None,
950 }
951 }
952
953 pub(crate) fn new_for_create_request(
954 servo: &Servo,
955 rendering_context: Rc<dyn RenderingContext>,
956 responder: IpcResponder<Option<NewWebViewDetails>>,
957 ) -> Self {
958 let mut builder = Self::new(servo, rendering_context);
959 builder.create_new_webview_responder = Some(responder);
960 builder
961 }
962
963 pub fn delegate(mut self, delegate: Rc<dyn WebViewDelegate>) -> Self {
964 self.delegate = delegate;
965 self
966 }
967
968 pub fn url(mut self, url: Url) -> Self {
969 self.url = Some(url);
970 self
971 }
972
973 pub fn hidpi_scale_factor(
974 mut self,
975 hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
976 ) -> Self {
977 self.hidpi_scale_factor = hidpi_scale_factor;
978 self
979 }
980
981 pub fn user_content_manager(mut self, user_content_manager: Rc<UserContentManager>) -> Self {
985 self.user_content_manager = Some(user_content_manager);
986 self
987 }
988
989 pub fn clipboard_delegate(mut self, clipboard_delegate: Rc<dyn ClipboardDelegate>) -> Self {
992 self.clipboard_delegate = Some(clipboard_delegate);
993 self
994 }
995
996 #[cfg(feature = "gamepad")]
999 pub fn gamepad_delegate(mut self, gamepad_delegate: Rc<dyn GamepadDelegate>) -> Self {
1000 self.gamepad_delegate = Some(gamepad_delegate);
1001 self
1002 }
1003
1004 pub fn build(self) -> WebView {
1005 WebView::new(self)
1006 }
1007}