E540 [webdriver] Move Webdriver to ServoShell by longvatrong111 · Pull Request #36714 · servo/servo · GitHub
[go: up one dir, main page]

Skip to content

Conversation

longvatrong111
Copy link
Contributor
@longvatrong111 longvatrong111 commented Apr 27, 2025

Moving webdriver to servoshell:

  • Let servoshell manage lifecycle of webdriver
  • One by one, move the handling of webdriver commands from constellation to embedder

Partially fix: #37370
Partially fix webdriver test timeout with no_top_browsing_context

cc: @xiaochengh

Copy link
Member
@yezhizhen yezhizhen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this!

@webbeef
Copy link
Contributor
webbeef commented Apr 29, 2025

Does that mean that every embedder will need to add Webdriver support code?

@longvatrong111
Copy link
Contributor Author

Does that mean that every embedder will need to add Webdriver support code?

It's a nice question. I think we can separate Webdriver support code as much as possible and reuse in different embedders.

@mrobinson
Copy link
Member

Does that mean that every embedder will need to add Webdriver support code?

I think this makes sense to some extent. For instance Webdriver can open and close WebViews. Only the embedder can do this properly.

@xiaochengh
Copy link
Contributor

Discussed with @longvatrong111 offline.

It's better to make WebDriver independent of ServoShell, as WebDriver shouldn't depend on any specific embedder.

WebDriver and real events should still share the same code paths as much as possible.

@mrobinson
Copy link
Member

It's better to make WebDriver independent of ServoShell, as WebDriver shouldn't depend on any specific embedder.

I still think it isn't a good idea to make it totally independent. At the very least, servoshell should use some generic WebDriver interface, but still execute the requested actions using its internal interface. WebDriver should be driving a browser, and not just the embedding layer.

@yezhizhen
Copy link
Member
yezhizhen commented Apr 30, 2025

As discussed with @longvatrong111 , I will move change in script_thread.rs to another PR and contribute upon it.

@xiaochengh
Copy link
Contributor

WebDriver should be driving a browser, and not just the embedding layer.

+1 to this. We'll see how it will be carried out.

@yezhizhen
Copy link
Member
yezhizhen commented Apr 30, 2025

As discussed with @longvatrong111 , I will move change in script_thread.rs to another PR and contribute upon it.

It seems new assertion failure is introduced for tests\wpt\tests\webdriver\tests\classic\element_click\events.py by get_events, with changes in script_thread.rs here.

assertion failed: !GetCurrentRealmOrNull(*cx).is_null() (thread Script(1,2), at components\script_bindings\realms.rs:25)

I will start by working from there.

@longvatrong111
Copy link
Contributor Author
longvatrong111 commented May 6, 2025

Rebase PR. Will check all reviews 1 by 1.
Right now I'm focusing on fixing synchronization issue between Input Event and WebDriver Script Event (the part about routing) with this PR.

@xiaochengh
Copy link
Contributor

Is there a clearer illustration / doc of what the relation between servoshell and web driver is, and what the relation should be?

@longvatrong111 longvatrong111 marked this pull request as draft May 23, 2025 04:04
@mrobinson
Copy link
Member

Is there a clearer illustration / doc of what the relation between servoshell and web driver is, and what the relation should be?

Yes, sorry for the confusion.

The WebDriver is an HTTP server that can drive the browser via commands from tests that are sent to the server.

Currently: The WebDriver is activated by the Servo embedding API. When active, the Constellation spawns a thread to listen for HTTP requests and send IPC messages directly to the Constellation or ScriptThreads. This presents an issue for several reasons including:

  1. When a client requests a new WebView to be created, the embedding API must request the embedder to create the WebView. It makes more sense for the WebDriver to create the WebView through the embedding API directly. This allows testing the embedding API and prevents a weird inversion of control. The only other time this happens is via things like window.open(), but these are more closely managed by the ScriptThread.
  2. Some events must be processed by the compositor before being sent to the internals of Servo. Since the WebDriver is running inside Servo, these events must be sent "out" of Servo to the embedding layer and it is difficult to ensure proper ordering of event handling since some events go straight to the Constellation and some go back out to the embedding layer first.

Future: servoshell would run a WebDriver thread that runs the HTTP server. Many commands become Servo embedding API commands and a good amount of special code can be removed from Servo to deal with the WebDriver. Note that this will still require an API to send some of the more specific WebDriver commands to the Constellation and then to the ScriptThread. This means that things like opening and closing WebViews work exactly the same way they do in a browser application. In order to avoid having the WebDriver code depend directly on servoshell, servoshell may be able to provide some callbacks which the WebDriver code can use.

@xiaochengh
Copy link
Contributor

Thanks for the clarification, that makes much more sense to me now!

So far my understanding of the objectives are: (1) Make servoshell manage the lifetime of WebDriver; (2) Make WebDriver drive Servo with the embedding API. Is that correct?

Another question: besides lifetime management, is there other communication needed between WebDriver and servoshell?

@mrobinson
Copy link
Member

Another question: besides lifetime management, is there other communication needed between WebDriver and servoshell?

That's a good question. I'm not exactly sure of the answer here. I think it depends on the details of all of the WebDriver APIs. If there are cases where WebDriver wants to do something like move the browser window around, then it will need to move the servoshell window.

@longvatrong111 longvatrong111 changed the title Draft: Move Webdriver to ServoShell [webdriver] Move Webdriver to ServoShell Jun 7, 2025
@longvatrong111 longvatrong111 marked this pull request as ready for review June 10, 2025 09:36
@longvatrong111
Copy link
Contributor Author
longvatrong111 commented Jun 10, 2025

@xiaochengh @mrobinson it is confirmed that the implementation of event route of commands in Context section are much simpler if sending events to servoshell instead of constellation. For example Close Window

Copy link
Member
@yezhizhen yezhizhen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is confirmed that the implementation of event route of commands in Context section are much simpler if sending events to servoshell instead of constellation. For example Close Window

I don't think so. Right now close window shares Constellation::handle_close_top_level_browsing_context with request ScriptToConstellationMessage and EmbedderToConstellationMessage::CloseWebView. Isn't that nice?

One by one, move the handling of webdriver commands from constellation to embedder

Some of them would still be easier to handle in Constellation as they require information from it. Need to be very careful with each case... :(

@longvatrong111
Copy link
Contributor Author

We need to hold a bit more, all tests are timeout in CI:
https://github.com/longvatrong111/servo/actions/runs/15704827627
It's weird because if I run some tests in local, the result is normal.

Copy link
Member
@yezhizhen yezhizhen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code itself looks good. Very excited for this!


Side notes:
I've been verifying the execution path, and found some redundancy for channel communication which has been there for quite a while. (Not really related to this PR)

But I wanna ask: why we sometimes use crossbeam_channel, and sometimes ipc? I assume the former is more efficient + mpmc, the latter is required for inter-process communication (to Content Process, e.g. script thread). If I'm correct, then we are misusing many ipc channels in webdriver_server/lib.rs if only talking to Constellation/embedder. Instead, should just use crossbeam_channel or even mpsc?
cc @jdm @sagudev

@longvatrong111
Copy link
Contributor Author

Thanks for the improvements!

I'm also wondering if we can further improve it by decoupling the current implementation with desktop ServoShell, so that it becomes something that can be easily plugged into any embedder implementation (eg SimpleServo on OHOS, which might be of our interests).

Probably something like:

struct WebDriverEmbedderPlugin {
  webdriver_receiver: Receiver<EmbedderMsg>,
  embedder_proxy: EmbedderProxy,
  ...
}

impl WebDriverEmbedderPlugin<'app> {
  // Called from embedder to start a WebDriver server.
  pub fn new(...) -> Self {...}

  // Called from emb
2D0B
edder only when running.
  pub fn handle_messages(&mut self, running_state: Rc<RunningAppState>) {...}

  fn forward_command(&self, command) {... }
}

...

pub struct App {
  ...
  webdriver_plugin: Option<WebDriverEmbedderPlugin>,
  ...
}

But there is no need to do it in this PR, since there are quite some obstacles before there, like we need a common trait for desktop and egl AppState, and probably more. But it's worth leaving a TODO for it.

I'm also looking for a way to easily plug webdriver into any embedder. Thank you for the suggestion.

@mrobinson
Copy link
Member

Apologies for not getting to this one yet. I will look at it first thing tomorrow morning.

@sagudev
Copy link
Member
sagudev commented Jun 18, 2025

why we sometimes use crossbeam_channel, and sometimes ipc?

You guessed right, it is for cross process reasons, but even though some stuff currently live in same process (actually all because we run servo in single process mode by default), but that might not be true in the future (there is also plan to have GPU process, etc), so we use ipc-channel in more places for easier potential process split up.

Here are other interesting ipc stuff:

@jdm
Copy link
Member
jdm commented Jun 18, 2025

The code itself looks good. Very excited for this!

Side notes: I've been verifying the execution path, and found some redundancy for channel communication which has been there for quite a while. (Not really related to this PR)

But I wanna ask: why we sometimes use crossbeam_channel, and sometimes ipc? I assume the former is more efficient + mpmc, the latter is required for inter-process communication (to Content Process, e.g. script thread). If I'm correct, then we are misusing many ipc channels in webdriver_server/lib.rs if only talking to Constellation/embedder. Instead, should just use crossbeam_channel or even mpsc? cc @jdm @sagudev

You are correct; uses like

WebDriverCommandMsg::GetWindowSize(webview_id, response_sender) => {
let browsing_context_id = BrowsingContextId::from(webview_id);
let size = self
.browsing_contexts
.get(&browsing_context_id)
.map(|browsing_context| browsing_context.viewport_details.size)
.unwrap_or_default();
let _ = response_sender.send(size);
do not require an IPC channel. However, WebDriverCommandMsg requires that all variants can be serialized right now (
#[derive(Debug, Deserialize, Serialize)]
), but it's not clear to me why that enum needs that derive.

Copy link
Member
@mrobinson mrobinson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great stuff!

I just have a few suggestions.

@longvatrong111 longvatrong111 force-pushed the webdriver-improve branch 3 times, most recently from 174bf83 to dd7b393 Compare June 19, 2025 05:51
@longvatrong111 longvatrong111 requested a review from mrobinson June 19, 2025 07:57
Copy link
Member
@mrobinson mrobinson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! One more comment before landing, but once that's done feel free to land without another review.

Signed-off-by: batu_hoang <longvatrong111@gmail.com>
Decompose webdriver::embedder_proxy to a sender and a waker.
Make the sender channel uses only webdriver command message.
Rebase main and solve conflict

Signed-off-by: batu_hoang <longvatrong111@gmail.com>
Signed-off-by: batu_hoang <longvatrong111@gmail.com>
Signed-off-by: batu_hoang <longvatrong111@gmail.com>
@mrobinson mrobinson force-pushed the webdriver-improve branch from 7c5844a to 83fb014 Compare June 19, 2025 09:17
@mrobinson
Copy link
DEB5 Member

I added some minor rustdoc comments and did a little re-ordering of members. Sending to the merge queue now. Thanks @longvatrong111!

@mrobinson mrobinson enabled auto-merge June 19, 2025 09:18
@mrobinson mrobinson added this pull request to the merge queue Jun 19, 2025
Merged via the queue into servo:main with commit d010079 Jun 19, 2025
21 checks passed
@longvatrong111 longvatrong111 deleted the webdriver-improve branch June 19, 2025 10:47
github-merge-queue bot pushed a commit that referenced this pull request Jun 25, 2025
…etWindowSize` to servoshell (#37555)

Follow up to: #36714
Moving webdriver [context
commands](https://www.w3.org/TR/webdriver2/#contexts) to be handled in
embedder:

- [x] New Window command - `WebdriverCommandMsg::NewWebView`
- [x]  Switch To Window command - `WebdriverCommandMsg::FocusWebView`
- [x] Resizing commands

cc: @xiaochengh

---------

Signed-off-by: batu_hoang <longvatrong111@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[webdriver] Moving webdriver to servoshell
7 participants
0