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: Image) {
397 self.inner_mut().favicon = Some(new_value);
398 self.delegate().notify_favicon_changed(self);
399 }
400
401 pub fn focused(&self) -> bool {
403 self.inner().focused
404 }
405
406 pub fn cursor(&self) -> Cursor {
412 self.inner().cursor
413 }
414
415 pub(crate) fn set_cursor(self, new_value: Cursor) {
416 if self.inner().cursor == new_value {
417 return;
418 }
419 self.inner_mut().cursor = new_value;
420 self.delegate().notify_cursor_changed(self, new_value);
421 }
422
423 pub fn set_focused(&self, focused: bool) {
429 let old_focused = std::mem::replace(&mut self.inner_mut().focused, focused);
430 if old_focused != focused {
431 self.servo().constellation_proxy().send(
432 EmbedderToConstellationMessage::SetWebViewHasSystemFocus(self.id(), focused),
433 );
434 }
435 }
436
437 pub fn animating(&self) -> bool {
442 self.inner().animating
443 }
444
445 pub(crate) fn set_animating(self, new_value: bool) {
446 if self.inner().animating == new_value {
447 return;
448 }
449 self.inner_mut().animating = new_value;
450 self.delegate().notify_animating_changed(self, new_value);
451 }
452
453 pub fn size(&self) -> DeviceSize {
455 self.inner().rendering_context.size2d().to_f32()
456 }
457
458 pub fn resize(&self, new_size: PhysicalSize<u32>) {
465 let new_size = PhysicalSize {
466 width: new_size.width.max(MINIMUM_WEBVIEW_SIZE.width as u32),
467 height: new_size.height.max(MINIMUM_WEBVIEW_SIZE.height as u32),
468 };
469
470 self.servo()
471 .paint()
472 .resize_rendering_context(self.id(), new_size);
473 }
474
475 pub fn hidpi_scale_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel> {
477 self.inner().hidpi_scale_factor
478 }
479
480 pub fn set_hidpi_scale_factor(
485 &self,
486 new_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
487 ) {
488 if self.inner().hidpi_scale_factor == new_scale_factor {
489 return;
490 }
491
492 self.inner_mut().hidpi_scale_factor = new_scale_factor;
493 self.servo()
494 .paint()
495 .set_hidpi_scale_factor(self.id(), new_scale_factor);
496 }
497
498 pub fn show(&self) {
500 self.servo()
501 .paint()
502 .show_webview(self.id())
503 .expect("BUG: invalid WebView instance");
504 }
505
506 pub fn hide(&self) {
508 self.servo()
509 .paint()
510 .hide_webview(self.id())
511 .expect("BUG: invalid WebView instance");
512 }
513
514 pub fn notify_theme_change(&self, theme: Theme) {
516 self.servo()
517 .constellation_proxy()
518 .send(EmbedderToConstellationMessage::ThemeChange(
519 self.id(),
520 theme,
521 ))
522 }
523
524 pub fn load(&self, url: Url) {
529 self.servo()
530 .constellation_proxy()
531 .send(EmbedderToConstellationMessage::LoadUrl(
532 self.id(),
533 UrlRequest::new(url),
534 ))
535 }
536
537 pub fn load_request(&self, url_request: UrlRequest) {
542 self.servo()
543 .constellation_proxy()
544 .send(EmbedderToConstellationMessage::LoadUrl(
545 self.id(),
546 url_request,
547 ))
548 }
549
550 pub fn reload(&self) {
552 self.inner_mut().load_status = LoadStatus::Started;
553 self.servo()
554 .constellation_proxy()
555 .send(EmbedderToConstellationMessage::Reload(self.id()))
556 }
557
558 pub fn can_go_back(&self) -> bool {
563 self.inner().back_forward_list_index != 0
564 }
565
566 pub fn go_back(&self, amount: usize) -> TraversalId {
572 let request = SessionHistoryTraversalRequest::new(
573 self.id(),
574 TraversalDirection::Back(amount),
575 HistoryTraversalSource::Embedder,
576 );
577 let traversal_id = request.id.clone();
578 self.servo()
579 .constellation_proxy()
580 .send(EmbedderToConstellationMessage::TraverseHistory(request));
581 traversal_id
582 }
583
584 pub fn can_go_forward(&self) -> bool {
589 let inner = self.inner();
590 inner.back_forward_list.len() > inner.back_forward_list_index + 1
591 }
592
593 pub fn go_forward(&self, amount: usize) -> TraversalId {
599 let request = SessionHistoryTraversalRequest::new(
600 self.id(),
601 TraversalDirection::Forward(amount),
602 HistoryTraversalSource::Embedder,
603 );
604 let traversal_id = request.id.clone();
605 self.servo()
606 .constellation_proxy()
607 .send(EmbedderToConstellationMessage::TraverseHistory(request));
608 traversal_id
609 }
610
611 pub fn notify_scroll_event(&self, scroll: Scroll, point: WebViewPoint) {
614 self.servo()
615 .paint()
616 .notify_scroll_event(self.id(), scroll, point);
617 }
618
619 pub fn notify_input_event(&self, event: InputEvent) -> InputEventId {
626 let event: InputEventAndId = event.into();
627 let event_id = event.id;
628 let webview_id = self.id();
629 let servo = &self.inner().servo;
630
631 if event.event.point().is_some() {
633 if !servo.paint().notify_input_event(self.id(), event) {
634 servo.add_pending_handled_input_event(PendingHandledInputEvent {
635 event_id,
636 webview_id,
637 });
638 servo.event_loop_waker().wake();
639 }
640 } else {
641 servo
642 .constellation_proxy()
643 .send(EmbedderToConstellationMessage::ForwardInputEvent(
644 webview_id, event, None, ));
646 }
647
648 event_id
649 }
650
651 pub fn notify_media_session_action_event(&self, event: MediaSessionActionType) {
653 self.servo()
654 .constellation_proxy()
655 .send(EmbedderToConstellationMessage::MediaSessionAction(event));
656 }
657
658 pub fn set_page_zoom(&self, new_zoom: f32) {
668 self.servo().paint().set_page_zoom(self.id(), new_zoom);
669 }
670
671 pub fn page_zoom(&self) -> f32 {
673 self.servo().paint().page_zoom(self.id())
674 }
675
676 pub fn adjust_pinch_zoom(&self, pinch_zoom_delta: f32, center: DevicePoint) {
687 self.servo()
688 .paint()
689 .adjust_pinch_zoom(self.id(), pinch_zoom_delta, center);
690 }
691
692 pub fn pinch_zoom(&self) -> f32 {
694 self.servo().paint().pinch_zoom(self.id())
695 }
696
697 pub fn device_pixels_per_css_pixel(&self) -> Scale<f32, CSSPixel, DevicePixel> {
702 self.servo().paint().device_pixels_per_page_pixel(self.id())
703 }
704
705 pub fn exit_fullscreen(&self) {
707 self.servo()
708 .constellation_proxy()
709 .send(EmbedderToConstellationMessage::ExitFullScreen(self.id()));
710 }
711
712 pub fn toggle_webrender_debugging(&self, debugging: WebRenderDebugOption) {
718 self.servo().paint().toggle_webrender_debug(debugging);
719 }
720
721 pub fn capture_webrender(&self) {
726 self.servo().paint().capture_webrender(self.id());
727 }
728
729 pub fn paint(&self) {
731 self.servo().paint().render(self.id());
732 }
733
734 pub fn user_content_manager(&self) -> Option<Rc<UserContentManager>> {
736 self.inner().user_content_manager.clone()
737 }
738
739 pub fn evaluate_javascript<T: ToString>(
742 &self,
743 script: T,
744 callback: impl FnOnce(Result<JSValue, JavaScriptEvaluationError>) + 'static,
745 ) {
746 self.servo().javascript_evaluator_mut().evaluate(
747 self.id(),
748 script.to_string(),
749 Box::new(callback),
750 );
751 }
752
753 pub fn take_screenshot(
771 &self,
772 rect: Option<WebViewRect>,
773 callback: impl FnOnce(Result<RgbaImage, ScreenshotCaptureError>) + 'static,
774 ) {
775 self.servo()
776 .paint()
777 .request_screenshot(self.id(), rect, Box::new(callback));
778 }
779
780 pub(crate) fn set_history(self, new_back_forward_list: Vec<ServoUrl>, new_index: usize) {
781 {
782 let mut inner_mut = self.inner_mut();
783 inner_mut.back_forward_list_index = new_index;
784 inner_mut.back_forward_list = new_back_forward_list
785 .into_iter()
786 .map(ServoUrl::into_url)
787 .collect();
788 }
789
790 let back_forward_list = self.inner().back_forward_list.clone();
791 let back_forward_list_index = self.inner().back_forward_list_index;
792 self.delegate().notify_url_changed(
793 self.clone(),
794 back_forward_list[back_forward_list_index].clone(),
795 );
796 self.delegate().notify_history_changed(
797 self.clone(),
798 back_forward_list,
799 back_forward_list_index,
800 );
801 }
802
803 pub(crate) fn show_embedder_control(
804 self,
805 control_id: EmbedderControlId,
806 position: DeviceIntRect,
807 embedder_control_request: EmbedderControlRequest,
808 ) {
809 let constellation_proxy = self.servo().constellation_proxy().clone();
810 let embedder_control = match embedder_control_request {
811 EmbedderControlRequest::SelectElement(request) => {
812 EmbedderControl::SelectElement(SelectElement {
813 id: control_id,
814 select_element_request: request,
815 position,
816 constellation_proxy,
817 response_sent: false,
818 })
819 },
820 EmbedderControlRequest::ColorPicker(current_color) => {
821 EmbedderControl::ColorPicker(ColorPicker {
822 id: control_id,
823 current_color: Some(current_color),
824 position,
825 constellation_proxy,
826 response_sent: false,
827 })
828 },
829 EmbedderControlRequest::InputMethod(input_method_request) => {
830 EmbedderControl::InputMethod(InputMethodControl {
831 id: control_id,
832 input_method_type: input_method_request.input_method_type,
833 text: input_method_request.text,
834 insertion_point: input_method_request.insertion_point,
835 position,
836 multiline: input_method_request.multiline,
837 allow_virtual_keyboard: input_method_request.allow_virtual_keyboard,
838 })
839 },
840 EmbedderControlRequest::ContextMenu(mut context_menu_request) => {
841 for item in context_menu_request.items.iter_mut() {
842 match item {
843 ContextMenuItem::Item {
844 action: ContextMenuAction::GoBack,
845 enabled,
846 ..
847 } => *enabled = self.can_go_back(),
848 ContextMenuItem::Item {
849 action: ContextMenuAction::GoForward,
850 enabled,
851 ..
852 } => *enabled = self.can_go_forward(),
853 _ => {},
854 }
855 }
856 EmbedderControl::ContextMenu(ContextMenu {
857 id: control_id,
858 position,
859 items: context_menu_request.items,
860 element_info: context_menu_request.element_info,
861 constellation_proxy,
862 response_sent: false,
863 })
864 },
865 EmbedderControlRequest::FilePicker { .. } => {
866 unreachable!("This message should be routed through the FileManagerThread")
867 },
868 };
869
870 self.delegate()
871 .show_embedder_control(self.clone(), embedder_control);
872 }
873
874 pub fn accesskit_tree_id(&self) -> Option<TreeId> {
876 self.inner().accesskit_tree_id
877 }
878
879 pub fn set_accessibility_active(&self, active: bool) -> Option<TreeId> {
894 if !pref!(accessibility_enabled) {
895 return None;
896 }
897
898 if active == self.inner().accesskit_tree_id.is_some() {
899 return self.accesskit_tree_id();
900 }
901
902 if active {
903 let accesskit_tree_id = TreeId(AccesskitUuid::new_v4());
904 self.inner_mut().accesskit_tree_id = Some(accesskit_tree_id);
905 } else {
906 self.inner_mut().accesskit_tree_id = None;
907 self.inner_mut().grafted_accesskit_tree_id = None;
908 self.inner_mut().grafted_accesskit_tree_epoch = None;
909 }
910
911 self.servo().constellation_proxy().send(
912 EmbedderToConstellationMessage::SetAccessibilityActive(self.id(), active),
913 );
914
915 self.accesskit_tree_id()
916 }
917
918 pub(crate) fn notify_document_accessibility_tree_id(&self, grafted_tree_id: TreeId) {
919 let old_grafted_tree_id = self
920 .inner_mut()
921 .grafted_accesskit_tree_id
922 .replace(grafted_tree_id);
923 if old_grafted_tree_id == Some(grafted_tree_id) {
926 return;
927 }
928 self.send_accessibility_root_node();
929 }
930
931 pub(crate) fn note_accessibility_viewport_changed(&self) {
940 self.inner().accessibility_viewport_changed.set(true);
941 }
942
943 pub(crate) fn take_accessibility_viewport_changed(&self) -> bool {
945 self.inner().accessibility_viewport_changed.take()
946 }
947
948 pub(crate) fn send_accessibility_root_node(&self) {
955 let Some(webview_accesskit_tree_id) = self.inner().accesskit_tree_id else {
956 return;
957 };
958 let Some(grafted_tree_id) = self.inner().grafted_accesskit_tree_id else {
959 return;
960 };
961 let root_node_id = NodeId(0);
962 let mut root_node = AccesskitNode::new(Role::ScrollView);
963 let device_pixels_per_css_pixel =
976 self.page_zoom() * self.hidpi_scale_factor().get() * self.pinch_zoom();
977 let size =
978 self.size() / Scale::<f32, CSSPixel, DevicePixel>::new(device_pixels_per_css_pixel);
979 root_node.set_bounds(AccesskitRect::new(
980 0.0,
981 0.0,
982 size.width as f64,
983 size.height as f64,
984 ));
985 if device_pixels_per_css_pixel != 1.0 {
987 root_node.set_transform(AccesskitAffine::scale(device_pixels_per_css_pixel as f64));
988 }
989 let graft_node_id = NodeId(1);
990 let mut graft_node = AccesskitNode::new(Role::GenericContainer);
991 graft_node.set_tree_id(grafted_tree_id);
992 root_node.set_children(vec![graft_node_id]);
993 self.delegate().notify_accessibility_tree_update(
994 self.clone(),
995 TreeUpdate {
996 nodes: vec![(root_node_id, root_node), (graft_node_id, graft_node)],
997 tree: Some(Tree {
998 root: root_node_id,
999 toolkit_name: None,
1000 toolkit_version: None,
1001 }),
1002 tree_id: webview_accesskit_tree_id,
1003 focus: root_node_id,
1004 },
1005 );
1006 }
1007
1008 fn move_accessibility_focus_to_graft_node(&self) {
1011 let Some(webview_accesskit_tree_id) = self.inner().accesskit_tree_id else {
1012 return;
1013 };
1014 let graft_node_id = NodeId(1);
1015
1016 self.delegate().notify_accessibility_tree_update(
1017 self.clone(),
1018 TreeUpdate {
1019 nodes: vec![],
1020 tree: None,
1021 tree_id: webview_accesskit_tree_id,
1022 focus: graft_node_id,
1023 },
1024 );
1025 }
1026
1027 pub(crate) fn process_accessibility_tree_update(&self, tree_update: TreeUpdate, epoch: Epoch) {
1028 if self
1029 .inner()
1030 .grafted_accesskit_tree_epoch
1031 .is_some_and(|current| epoch < current)
1032 {
1033 debug!("Ignoring stale tree update for {:?}", tree_update.tree_id);
1037 return;
1038 }
1039 if self
1040 .inner()
1041 .grafted_accesskit_tree_epoch
1042 .is_none_or(|current| epoch > current)
1043 {
1044 self.notify_document_accessibility_tree_id(tree_update.tree_id);
1048 self.inner_mut().grafted_accesskit_tree_epoch = Some(epoch);
1049
1050 self.delegate()
1051 .notify_accessibility_tree_update(self.clone(), tree_update);
1052
1053 self.move_accessibility_focus_to_graft_node();
1054 return;
1055 }
1056
1057 self.delegate()
1058 .notify_accessibility_tree_update(self.clone(), tree_update);
1059 }
1060
1061 pub fn clear_session_history(&self) {
1066 self.servo().constellation_proxy().send(
1067 EmbedderToConstellationMessage::ClearSessionHistory(self.id()),
1068 );
1069 }
1070}
1071
1072struct ServoRendererWebView {
1075 id: WebViewId,
1076 weak_handle: Weak<RefCell<WebViewInner>>,
1077}
1078
1079impl WebViewTrait for ServoRendererWebView {
1080 fn id(&self) -> WebViewId {
1081 self.id
1082 }
1083
1084 fn screen_geometry(&self) -> Option<ScreenGeometry> {
1085 let webview = WebView::from_weak_handle(&self.weak_handle)?;
1086 webview.delegate().screen_geometry(webview)
1087 }
1088
1089 fn set_animating(&self, new_value: bool) {
1090 if let Some(webview) = WebView::from_weak_handle(&self.weak_handle) {
1091 webview.set_animating(new_value);
1092 }
1093 }
1094
1095 fn notify_viewport_updated(&self) {
1096 if let Some(webview) = WebView::from_weak_handle(&self.weak_handle) {
1097 webview.note_accessibility_viewport_changed();
1098 }
1099 }
1100}
1101
1102pub struct WebViewBuilder {
1104 servo: Servo,
1105 rendering_context: Rc<dyn RenderingContext>,
1106 delegate: Rc<dyn WebViewDelegate>,
1107 url: Option<Url>,
1108 hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
1109 create_new_webview_responder: Option<AutomaticResponder<Option<NewWebViewDetails>>>,
1110 user_content_manager: Option<Rc<UserContentManager>>,
1111 clipboard_delegate: Option<Rc<dyn ClipboardDelegate>>,
1112 #[cfg(feature = "gamepad")]
1113 gamepad_delegate: Option<Rc<dyn GamepadDelegate>>,
1114 geolocation_delegate: Option<Rc<dyn GeolocationDelegate>>,
1115}
1116
1117impl WebViewBuilder {
1118 pub fn new(servo: &Servo, rendering_context: Rc<dyn RenderingContext>) -> Self {
1123 Self {
1124 servo: servo.clone(),
1125 rendering_context,
1126 url: None,
1127 hidpi_scale_factor: Scale::new(1.0),
1128 delegate: Rc::new(DefaultWebViewDelegate),
1129 create_new_webview_responder: None,
1130 user_content_manager: None,
1131 clipboard_delegate: None,
1132 #[cfg(feature = "gamepad")]
1133 gamepad_delegate: None,
1134 geolocation_delegate: None,
1135 }
1136 }
1137
1138 pub(crate) fn new_for_create_request(
1139 servo: &Servo,
1140 rendering_context: Rc<dyn RenderingContext>,
1141 responder: AutomaticResponder<Option<NewWebViewDetails>>,
1142 ) -> Self {
1143 let mut builder = Self::new(servo, rendering_context);
1144 builder.create_new_webview_responder = Some(responder);
1145 builder
1146 }
1147
1148 pub fn delegate(mut self, delegate: Rc<dyn WebViewDelegate>) -> Self {
1151 self.delegate = delegate;
1152 self
1153 }
1154
1155 pub fn url(mut self, url: Url) -> Self {
1157 self.url = Some(url);
1158 self
1159 }
1160
1161 pub fn hidpi_scale_factor(
1163 mut self,
1164 hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
1165 ) -> Self {
1166 self.hidpi_scale_factor = hidpi_scale_factor;
1167 self
1168 }
1169
1170 pub fn user_content_manager(mut self, user_content_manager: Rc<UserContentManager>) -> Self {
1174 self.user_content_manager = Some(user_content_manager);
1175 self
1176 }
1177
1178 pub fn clipboard_delegate(mut self, clipboard_delegate: Rc<dyn ClipboardDelegate>) -> Self {
1181 self.clipboard_delegate = Some(clipboard_delegate);
1182 self
1183 }
1184
1185 #[cfg(feature = "gamepad")]
1188 pub fn gamepad_delegate(mut self, gamepad_delegate: Rc<dyn GamepadDelegate>) -> Self {
1189 self.gamepad_delegate = Some(gamepad_delegate);
1190 self
1191 }
1192
1193 pub fn geolocation_delegate(
1196 mut self,
1197 geolocation_delegate: Rc<dyn GeolocationDelegate>,
1198 ) -> Self {
1199 self.geolocation_delegate = Some(geolocation_delegate);
1200 self
1201 }
1202
1203 pub fn build(self) -> WebView {
1205 WebView::new(self)
1206 }
1207}