servo/components/servo/javascript_evaluator.rs
Narfinger 991be359a3
libservo: Allow embedders to execute JavaScript scripts via the API (#35720)
This change adds a new `WebView` API `evaluate_javascript()`, which
allows embedders to
execute JavaScript code and wait for a reply asynchronously. Ongoing
script execution is
tracked by a libservo `JavaScriptEvaluator` struct, which maps an id to
the callback passed
to the `evaluate_javascript()` method. The id is used to track the
script and its execution
through the other parts of Servo.

Testing: This changes includes `WebView` unit tests.

---------

Signed-off-by: Narfinger <Narfinger@users.noreply.github.com>
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
2025-05-13 12:54:18 +00:00

65 lines
2.1 KiB
Rust

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::collections::HashMap;
use base::id::WebViewId;
use constellation_traits::EmbedderToConstellationMessage;
use embedder_traits::{JSValue, JavaScriptEvaluationError, JavaScriptEvaluationId};
use crate::ConstellationProxy;
struct PendingEvaluation {
callback: Box<dyn FnOnce(Result<JSValue, JavaScriptEvaluationError>)>,
}
pub(crate) struct JavaScriptEvaluator {
current_id: JavaScriptEvaluationId,
constellation_proxy: ConstellationProxy,
pending_evaluations: HashMap<JavaScriptEvaluationId, PendingEvaluation>,
}
impl JavaScriptEvaluator {
pub(crate) fn new(constellation_proxy: ConstellationProxy) -> Self {
Self {
current_id: JavaScriptEvaluationId(0),
constellation_proxy,
pending_evaluations: Default::default(),
}
}
fn generate_id(&mut self) -> JavaScriptEvaluationId {
let next_id = JavaScriptEvaluationId(self.current_id.0 + 1);
std::mem::replace(&mut self.current_id, next_id)
}
pub(crate) fn evaluate(
&mut self,
webview_id: WebViewId,
script: String,
callback: Box<dyn FnOnce(Result<JSValue, JavaScriptEvaluationError>)>,
) {
let evaluation_id = self.generate_id();
self.constellation_proxy
.send(EmbedderToConstellationMessage::EvaluateJavaScript(
webview_id,
evaluation_id,
script,
));
self.pending_evaluations
.insert(evaluation_id, PendingEvaluation { callback });
}
pub(crate) fn finish_evaluation(
&mut self,
evaluation_id: JavaScriptEvaluationId,
result: Result<JSValue, JavaScriptEvaluationError>,
) {
(self
.pending_evaluations
.remove(&evaluation_id)
.expect("Received request to finish unknown JavaScript evaluation.")
.callback)(result)
}
}