Rework on webdriver wait for navigation complete (#38234)

For current implementation, when a command may trigger a navigation,
webdriver only waits for document readiness state.
However, not all navigations make change in document.
This PR handles more cases for waiting for a navigation, and apply to
`element_click`.

- Before sending a command which may trigger a navigation, `webdriver`
sets `load status send` to `embedder`, `constelltation` and `script
thread` to listen to `navigation events`.
- Webdriver check if there is a navigation with `script thread`.
- If the navigation is loading a new url, webdriver checks if the
request is approved with `constellation`, then waits for document
readiness state.
- If the navigation is a hashchange, webdriver waits untill all new
generated dom events have been processed.

Testing: 
`tests/wpt/tests/webdriver/tests/classic/element_click/navigate.py`
`tests/wpt/tests/webdriver/tests/classic/element_click/user_prompts.py`
https://github.com/longvatrong111/servo/actions/runs/16488690749

cc: @xiaochengh

---------

Signed-off-by: batu_hoang <hoang.binh.trong@huawei.com>
This commit is contained in:
batu_hoang 2025-07-30 15:24:07 +08:00 committed by GitHub
parent 8b3e7b1c6a
commit 37ac4ffeb4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 188 additions and 125 deletions

View file

@ -34,7 +34,7 @@ use embedder_traits::user_content_manager::{UserContentManager, UserScript};
use embedder_traits::{
AlertResponse, ConfirmResponse, EmbedderMsg, GamepadEvent, GamepadSupportedHapticEffects,
GamepadUpdateType, PromptResponse, SimpleDialog, Theme, ViewportDetails, WebDriverJSError,
WebDriverJSResult,
WebDriverJSResult, WebDriverLoadStatus,
};
use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect, Size2D as UntypedSize2D};
use euclid::{Point2D, Scale, Size2D, Vector2D};
@ -311,6 +311,10 @@ pub(crate) struct Window {
#[no_trace]
webdriver_script_chan: DomRefCell<Option<IpcSender<WebDriverJSResult>>>,
/// A channel to notify webdriver if there is a navigation
#[no_trace]
webdriver_load_status_sender: RefCell<Option<IpcSender<WebDriverLoadStatus>>>,
/// The current state of the window object
current_state: Cell<WindowState>,
@ -2641,6 +2645,11 @@ impl Window {
// Step 6
// TODO: Fragment handling appears to have moved to step 13
if let Some(fragment) = load_data.url.fragment() {
let webdriver_sender = self.webdriver_load_status_sender.borrow().clone();
if let Some(ref sender) = webdriver_sender {
let _ = sender.send(WebDriverLoadStatus::NavigationStart);
}
self.send_to_constellation(ScriptToConstellationMessage::NavigatedToFragment(
load_data.url.clone(),
history_handling,
@ -2660,6 +2669,9 @@ impl Window {
new_url,
CanGc::note());
event.upcast::<Event>().fire(this.upcast::<EventTarget>(), CanGc::note());
if let Some(sender) = webdriver_sender {
let _ = sender.send(WebDriverLoadStatus::NavigationStop);
}
});
self.as_global_scope()
.task_manager()
@ -2714,6 +2726,10 @@ impl Window {
NavigationHistoryBehavior::Push
};
if let Some(sender) = self.webdriver_load_status_sender.borrow().as_ref() {
let _ = sender.send(WebDriverLoadStatus::NavigationStart);
}
// Step 13
ScriptThread::navigate(pipeline_id, load_data, resolved_history_handling);
};
@ -2845,6 +2861,13 @@ impl Window {
*self.webdriver_script_chan.borrow_mut() = chan;
}
pub(crate) fn set_webdriver_load_status_sender(
&self,
sender: Option<IpcSender<WebDriverLoadStatus>>,
) {
*self.webdriver_load_status_sender.borrow_mut() = sender;
}
pub(crate) fn is_alive(&self) -> bool {
self.current_state.get() == WindowState::Alive
}
@ -3139,6 +3162,7 @@ impl Window {
devtools_marker_sender: Default::default(),
devtools_markers: Default::default(),
webdriver_script_chan: Default::default(),
webdriver_load_status_sender: Default::default(),
error_reporter,
media_query_lists: DOMTracker::new(),
#[cfg(feature = "bluetooth")]