mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
libservo: Convert intercept_web_resource_load
into load_web_resource
(#35564)
Rework the `WebViewDelegate::intercept_web_resource_load` into `WebViewDelegate::load_web_resource` and clean up internal messaging. The main thing here is adding objects which manage the response to these delegate methods. Now we have `WebResourceLoad` and `InterceptedWebResourceLoad` which make it much harder to misuse the API. In addition, the internal messaging for this is cleaned up. Canceling and finishing the load are unrelated to the HTTP body so they are no longer subtypes of an HttpBodyData message. Processing of messages is made a bit more efficient by collecting all body chunks in a vector and only flattening the chunks at the end. Finally, "interceptor" is a much more common spelling than "intercepter" so I've gone ahead and made this change everywhere. Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
parent
f6e2e3d418
commit
cf2b93f18a
14 changed files with 245 additions and 208 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1865,6 +1865,7 @@ dependencies = [
|
|||
"serde",
|
||||
"servo_malloc_size_of",
|
||||
"servo_url",
|
||||
"url",
|
||||
"webrender_api",
|
||||
"webxr-api",
|
||||
]
|
||||
|
|
|
@ -648,6 +648,14 @@ impl MallocSizeOf for url::Host {
|
|||
}
|
||||
}
|
||||
|
||||
impl MallocSizeOf for url::Url {
|
||||
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
|
||||
// TODO: This is an estimate, but a real size should be calculated in `rust-url` once
|
||||
// it has support for `malloc_size_of`.
|
||||
self.to_string().size_of(ops)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: MallocSizeOf, U> MallocSizeOf for euclid::Vector2D<T, U> {
|
||||
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
|
||||
self.x.size_of(ops) + self.y.size_of(ops)
|
||||
|
|
|
@ -43,7 +43,7 @@ use crate::fetch::headers::determine_nosniff;
|
|||
use crate::filemanager_thread::FileManager;
|
||||
use crate::http_loader::{determine_requests_referrer, http_fetch, set_default_accept, HttpState};
|
||||
use crate::protocols::ProtocolRegistry;
|
||||
use crate::request_intercepter::RequestIntercepter;
|
||||
use crate::request_interceptor::RequestInterceptor;
|
||||
use crate::subresource_integrity::is_response_integrity_valid;
|
||||
|
||||
pub type Target<'a> = &'a mut (dyn FetchTaskTarget + Send);
|
||||
|
@ -61,7 +61,7 @@ pub struct FetchContext {
|
|||
pub devtools_chan: Option<Arc<Mutex<Sender<DevtoolsControlMsg>>>>,
|
||||
pub filemanager: Arc<Mutex<FileManager>>,
|
||||
pub file_token: FileTokenCheck,
|
||||
pub request_intercepter: Arc<Mutex<RequestIntercepter>>,
|
||||
pub request_interceptor: Arc<Mutex<RequestInterceptor>>,
|
||||
pub cancellation_listener: Arc<CancellationListener>,
|
||||
pub timing: ServoArc<Mutex<ResourceFetchTiming>>,
|
||||
pub protocols: Arc<ProtocolRegistry>,
|
||||
|
@ -328,7 +328,7 @@ pub async fn main_fetch(
|
|||
|
||||
// Intercept the request and maybe override the response.
|
||||
context
|
||||
.request_intercepter
|
||||
.request_interceptor
|
||||
.lock()
|
||||
.unwrap()
|
||||
.intercept_request(request, &mut response, context);
|
||||
|
|
|
@ -18,7 +18,7 @@ pub mod image_cache;
|
|||
pub mod local_directory_listing;
|
||||
pub mod mime_classifier;
|
||||
pub mod protocols;
|
||||
pub mod request_intercepter;
|
||||
pub mod request_interceptor;
|
||||
pub mod resource_thread;
|
||||
mod storage_thread;
|
||||
pub mod subresource_integrity;
|
||||
|
|
|
@ -1,105 +0,0 @@
|
|||
/* 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 content_security_policy::Destination;
|
||||
use embedder_traits::{
|
||||
EmbedderMsg, EmbedderProxy, HttpBodyData, WebResourceRequest, WebResourceResponseMsg,
|
||||
};
|
||||
use ipc_channel::ipc;
|
||||
use net_traits::http_status::HttpStatus;
|
||||
use net_traits::request::Request;
|
||||
use net_traits::response::{Response, ResponseBody};
|
||||
use net_traits::NetworkError;
|
||||
|
||||
use crate::fetch::methods::FetchContext;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RequestIntercepter {
|
||||
embedder_proxy: EmbedderProxy,
|
||||
}
|
||||
|
||||
impl RequestIntercepter {
|
||||
pub fn new(embedder_proxy: EmbedderProxy) -> RequestIntercepter {
|
||||
RequestIntercepter { embedder_proxy }
|
||||
}
|
||||
|
||||
pub fn intercept_request(
|
||||
&self,
|
||||
request: &mut Request,
|
||||
response: &mut Option<Response>,
|
||||
context: &FetchContext,
|
||||
) {
|
||||
let (tx, rx) = ipc::channel().unwrap();
|
||||
let is_for_main_frame = matches!(request.destination, Destination::Document);
|
||||
let req = WebResourceRequest::new(
|
||||
request.method.clone(),
|
||||
request.headers.clone(),
|
||||
request.url(),
|
||||
is_for_main_frame,
|
||||
request.redirect_count > 0,
|
||||
);
|
||||
|
||||
self.embedder_proxy.send(EmbedderMsg::WebResourceRequested(
|
||||
request.target_webview_id,
|
||||
req,
|
||||
tx,
|
||||
));
|
||||
let mut response_received = false;
|
||||
|
||||
// TODO: use done_chan and run in CoreResourceThreadPool.
|
||||
while let Ok(msg) = rx.recv() {
|
||||
match msg {
|
||||
WebResourceResponseMsg::Start(webresource_response) => {
|
||||
response_received = true;
|
||||
let timing = context.timing.lock().unwrap().clone();
|
||||
let mut res = Response::new(webresource_response.url.clone(), timing);
|
||||
res.headers = webresource_response.headers;
|
||||
res.status = HttpStatus::new(
|
||||
webresource_response.status_code,
|
||||
webresource_response.status_message,
|
||||
);
|
||||
*res.body.lock().unwrap() = ResponseBody::Receiving(Vec::new());
|
||||
*response = Some(res);
|
||||
},
|
||||
WebResourceResponseMsg::Body(data) => {
|
||||
if !response_received {
|
||||
panic!("Receive body before initializing a Response!");
|
||||
}
|
||||
if let Some(ref mut res) = *response {
|
||||
match data {
|
||||
HttpBodyData::Chunk(chunk) => {
|
||||
if let ResponseBody::Receiving(ref mut body) =
|
||||
*res.body.lock().unwrap()
|
||||
{
|
||||
body.extend_from_slice(&chunk);
|
||||
} else {
|
||||
panic!("Receive Playload Message when Response body is not in Receiving state!");
|
||||
}
|
||||
},
|
||||
HttpBodyData::Done => {
|
||||
let mut res_body = res.body.lock().unwrap();
|
||||
if let ResponseBody::Receiving(ref mut body) = *res_body {
|
||||
let completed_body = std::mem::take(body);
|
||||
*res_body = ResponseBody::Done(completed_body);
|
||||
} else {
|
||||
panic!("Receive Done Message when Response body is not in Receiving state!");
|
||||
}
|
||||
break;
|
||||
},
|
||||
HttpBodyData::Cancelled => {
|
||||
*response =
|
||||
Some(Response::network_error(NetworkError::LoadCancelled));
|
||||
break;
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
WebResourceResponseMsg::None => {
|
||||
// Will not intercept the response. Continue.
|
||||
break;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
86
components/net/request_interceptor.rs
Normal file
86
components/net/request_interceptor.rs
Normal file
|
@ -0,0 +1,86 @@
|
|||
/* 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 content_security_policy::Destination;
|
||||
use embedder_traits::{EmbedderMsg, EmbedderProxy, WebResourceRequest, WebResourceResponseMsg};
|
||||
use ipc_channel::ipc;
|
||||
use log::error;
|
||||
use net_traits::http_status::HttpStatus;
|
||||
use net_traits::request::Request;
|
||||
use net_traits::response::{Response, ResponseBody};
|
||||
use net_traits::NetworkError;
|
||||
|
||||
use crate::fetch::methods::FetchContext;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RequestInterceptor {
|
||||
embedder_proxy: EmbedderProxy,
|
||||
}
|
||||
|
||||
impl RequestInterceptor {
|
||||
pub fn new(embedder_proxy: EmbedderProxy) -> RequestInterceptor {
|
||||
RequestInterceptor { embedder_proxy }
|
||||
}
|
||||
|
||||
pub fn intercept_request(
|
||||
&self,
|
||||
request: &mut Request,
|
||||
response: &mut Option<Response>,
|
||||
context: &FetchContext,
|
||||
) {
|
||||
let (sender, receiver) = ipc::channel().unwrap();
|
||||
let is_for_main_frame = matches!(request.destination, Destination::Document);
|
||||
let web_resource_request = WebResourceRequest {
|
||||
method: request.method.clone(),
|
||||
url: request.url().into_url(),
|
||||
headers: request.headers.clone(),
|
||||
is_for_main_frame,
|
||||
is_redirect: request.redirect_count > 0,
|
||||
};
|
||||
|
||||
self.embedder_proxy.send(EmbedderMsg::WebResourceRequested(
|
||||
request.target_webview_id,
|
||||
web_resource_request,
|
||||
sender,
|
||||
));
|
||||
|
||||
// TODO: use done_chan and run in CoreResourceThreadPool.
|
||||
let mut accumulated_body = Vec::new();
|
||||
while let Ok(message) = receiver.recv() {
|
||||
match message {
|
||||
WebResourceResponseMsg::Start(webresource_response) => {
|
||||
let timing = context.timing.lock().unwrap().clone();
|
||||
let mut response_override =
|
||||
Response::new(webresource_response.url.into(), timing);
|
||||
response_override.headers = webresource_response.headers;
|
||||
response_override.status = HttpStatus::new(
|
||||
webresource_response.status_code,
|
||||
webresource_response.status_message,
|
||||
);
|
||||
*response = Some(response_override);
|
||||
},
|
||||
WebResourceResponseMsg::SendBodyData(data) => {
|
||||
accumulated_body.push(data);
|
||||
},
|
||||
WebResourceResponseMsg::FinishLoad => {
|
||||
if accumulated_body.is_empty() {
|
||||
break;
|
||||
}
|
||||
let Some(response) = response.as_mut() else {
|
||||
error!("Received unexpected FinishLoad message");
|
||||
break;
|
||||
};
|
||||
*response.body.lock().unwrap() =
|
||||
ResponseBody::Done(accumulated_body.into_iter().flatten().collect());
|
||||
break;
|
||||
},
|
||||
WebResourceResponseMsg::CancelLoad => {
|
||||
*response = Some(Response::network_error(NetworkError::LoadCancelled));
|
||||
break;
|
||||
},
|
||||
WebResourceResponseMsg::DoNotIntercept => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -54,7 +54,7 @@ use crate::hsts::HstsList;
|
|||
use crate::http_cache::HttpCache;
|
||||
use crate::http_loader::{http_redirect_fetch, HttpState};
|
||||
use crate::protocols::ProtocolRegistry;
|
||||
use crate::request_intercepter::RequestIntercepter;
|
||||
use crate::request_interceptor::RequestInterceptor;
|
||||
use crate::storage_thread::StorageThreadFactory;
|
||||
use crate::websocket_loader;
|
||||
|
||||
|
@ -553,7 +553,7 @@ pub struct CoreResourceManager {
|
|||
devtools_sender: Option<Sender<DevtoolsControlMsg>>,
|
||||
sw_managers: HashMap<ImmutableOrigin, IpcSender<CustomResponseMediator>>,
|
||||
filemanager: FileManager,
|
||||
request_intercepter: RequestIntercepter,
|
||||
request_interceptor: RequestInterceptor,
|
||||
thread_pool: Arc<CoreResourceThreadPool>,
|
||||
ca_certificates: CACertificates,
|
||||
ignore_certificate_errors: bool,
|
||||
|
@ -706,7 +706,7 @@ impl CoreResourceManager {
|
|||
devtools_sender,
|
||||
sw_managers: Default::default(),
|
||||
filemanager: FileManager::new(embedder_proxy.clone(), Arc::downgrade(&pool_handle)),
|
||||
request_intercepter: RequestIntercepter::new(embedder_proxy),
|
||||
request_interceptor: RequestInterceptor::new(embedder_proxy),
|
||||
thread_pool: pool_handle,
|
||||
ca_certificates,
|
||||
ignore_certificate_errors,
|
||||
|
@ -749,7 +749,7 @@ impl CoreResourceManager {
|
|||
let ua = self.user_agent.clone();
|
||||
let dc = self.devtools_sender.clone();
|
||||
let filemanager = self.filemanager.clone();
|
||||
let request_intercepter = self.request_intercepter.clone();
|
||||
let request_interceptor = self.request_interceptor.clone();
|
||||
|
||||
let timing_type = match request_builder.destination {
|
||||
Destination::Document => ResourceTimingType::Navigation,
|
||||
|
@ -791,7 +791,7 @@ impl CoreResourceManager {
|
|||
devtools_chan: dc.map(|dc| Arc::new(Mutex::new(dc))),
|
||||
filemanager: Arc::new(Mutex::new(filemanager)),
|
||||
file_token,
|
||||
request_intercepter: Arc::new(Mutex::new(request_intercepter)),
|
||||
request_interceptor: Arc::new(Mutex::new(request_interceptor)),
|
||||
cancellation_listener,
|
||||
timing: ServoArc::new(Mutex::new(ResourceFetchTiming::new(request.timing_type()))),
|
||||
protocols,
|
||||
|
|
|
@ -30,7 +30,7 @@ use net::fetch::methods::{self, FetchContext};
|
|||
use net::filemanager_thread::FileManager;
|
||||
use net::hsts::HstsEntry;
|
||||
use net::protocols::ProtocolRegistry;
|
||||
use net::request_intercepter::RequestIntercepter;
|
||||
use net::request_interceptor::RequestInterceptor;
|
||||
use net::resource_thread::CoreResourceThreadPool;
|
||||
use net_traits::filemanager_thread::FileTokenCheck;
|
||||
use net_traits::http_status::HttpStatus;
|
||||
|
@ -708,7 +708,7 @@ fn test_fetch_with_hsts() {
|
|||
Weak::new(),
|
||||
))),
|
||||
file_token: FileTokenCheck::NotRequired,
|
||||
request_intercepter: Arc::new(Mutex::new(RequestIntercepter::new(embedder_proxy))),
|
||||
request_interceptor: Arc::new(Mutex::new(RequestInterceptor::new(embedder_proxy))),
|
||||
cancellation_listener: Arc::new(Default::default()),
|
||||
timing: ServoArc::new(Mutex::new(ResourceFetchTiming::new(
|
||||
ResourceTimingType::Navigation,
|
||||
|
@ -768,7 +768,7 @@ fn test_load_adds_host_to_hsts_list_when_url_is_https() {
|
|||
Weak::new(),
|
||||
))),
|
||||
file_token: FileTokenCheck::NotRequired,
|
||||
request_intercepter: Arc::new(Mutex::new(RequestIntercepter::new(embedder_proxy))),
|
||||
request_interceptor: Arc::new(Mutex::new(RequestInterceptor::new(embedder_proxy))),
|
||||
cancellation_listener: Arc::new(Default::default()),
|
||||
timing: ServoArc::new(Mutex::new(ResourceFetchTiming::new(
|
||||
ResourceTimingType::Navigation,
|
||||
|
@ -830,7 +830,7 @@ fn test_fetch_self_signed() {
|
|||
Weak::new(),
|
||||
))),
|
||||
file_token: FileTokenCheck::NotRequired,
|
||||
request_intercepter: Arc::new(Mutex::new(RequestIntercepter::new(embedder_proxy))),
|
||||
request_interceptor: Arc::new(Mutex::new(RequestInterceptor::new(embedder_proxy))),
|
||||
cancellation_listener: Arc::new(Default::default()),
|
||||
timing: ServoArc::new(Mutex::new(ResourceFetchTiming::new(
|
||||
ResourceTimingType::Navigation,
|
||||
|
@ -1363,17 +1363,13 @@ fn test_fetch_request_intercepted() {
|
|||
.status_message(STATUS_MESSAGE.to_vec());
|
||||
let msg = embedder_traits::WebResourceResponseMsg::Start(response);
|
||||
let _ = response_sender.send(msg);
|
||||
let msg2 = embedder_traits::WebResourceResponseMsg::Body(
|
||||
embedder_traits::HttpBodyData::Chunk(BODY_PART1.to_vec()),
|
||||
);
|
||||
let msg2 =
|
||||
embedder_traits::WebResourceResponseMsg::SendBodyData(BODY_PART1.to_vec());
|
||||
let _ = response_sender.send(msg2);
|
||||
let msg3 = embedder_traits::WebResourceResponseMsg::Body(
|
||||
embedder_traits::HttpBodyData::Chunk(BODY_PART2.to_vec()),
|
||||
);
|
||||
let msg3 =
|
||||
embedder_traits::WebResourceResponseMsg::SendBodyData(BODY_PART2.to_vec());
|
||||
let _ = response_sender.send(msg3);
|
||||
let _ = response_sender.send(embedder_traits::WebResourceResponseMsg::Body(
|
||||
embedder_traits::HttpBodyData::Done,
|
||||
));
|
||||
let _ = response_sender.send(embedder_traits::WebResourceResponseMsg::FinishLoad);
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
@ -1388,7 +1384,7 @@ fn test_fetch_request_intercepted() {
|
|||
Weak::new(),
|
||||
))),
|
||||
file_token: FileTokenCheck::NotRequired,
|
||||
request_intercepter: Arc::new(Mutex::new(RequestIntercepter::new(embedder_proxy))),
|
||||
request_interceptor: Arc::new(Mutex::new(RequestInterceptor::new(embedder_proxy))),
|
||||
cancellation_listener: Arc::new(Default::default()),
|
||||
timing: ServoArc::new(Mutex::new(ResourceFetchTiming::new(
|
||||
ResourceTimingType::Navigation,
|
||||
|
|
|
@ -42,7 +42,7 @@ use net::fetch::cors_cache::CorsCache;
|
|||
use net::fetch::methods::{self, FetchContext};
|
||||
use net::filemanager_thread::FileManager;
|
||||
use net::protocols::ProtocolRegistry;
|
||||
use net::request_intercepter::RequestIntercepter;
|
||||
use net::request_interceptor::RequestInterceptor;
|
||||
use net::resource_thread::CoreResourceThreadPool;
|
||||
use net::test::HttpState;
|
||||
use net_traits::filemanager_thread::FileTokenCheck;
|
||||
|
@ -177,7 +177,7 @@ fn new_fetch_context(
|
|||
pool_handle.unwrap_or_else(|| Weak::new()),
|
||||
))),
|
||||
file_token: FileTokenCheck::NotRequired,
|
||||
request_intercepter: Arc::new(Mutex::new(RequestIntercepter::new(sender))),
|
||||
request_interceptor: Arc::new(Mutex::new(RequestInterceptor::new(sender))),
|
||||
cancellation_listener: Arc::new(Default::default()),
|
||||
timing: ServoArc::new(Mutex::new(ResourceFetchTiming::new(
|
||||
ResourceTimingType::Navigation,
|
||||
|
|
|
@ -126,7 +126,7 @@ pub use crate::servo_delegate::{ServoDelegate, ServoError};
|
|||
pub use crate::webview::WebView;
|
||||
pub use crate::webview_delegate::{
|
||||
AllowOrDenyRequest, AuthenticationRequest, NavigationRequest, PermissionRequest,
|
||||
WebViewDelegate,
|
||||
WebResourceLoad, WebViewDelegate,
|
||||
};
|
||||
|
||||
#[cfg(feature = "webdriver")]
|
||||
|
@ -836,20 +836,17 @@ impl Servo {
|
|||
web_resource_request,
|
||||
response_sender,
|
||||
) => {
|
||||
let webview = webview_id.and_then(|webview_id| self.get_webview_handle(webview_id));
|
||||
if let Some(webview) = webview.clone() {
|
||||
webview.delegate().intercept_web_resource_load(
|
||||
webview,
|
||||
&web_resource_request,
|
||||
response_sender.clone(),
|
||||
);
|
||||
}
|
||||
|
||||
self.delegate().intercept_web_resource_load(
|
||||
webview,
|
||||
&web_resource_request,
|
||||
let web_resource_load = WebResourceLoad {
|
||||
request: web_resource_request,
|
||||
response_sender,
|
||||
);
|
||||
intercepted: false,
|
||||
};
|
||||
match webview_id.and_then(|webview_id| self.get_webview_handle(webview_id)) {
|
||||
Some(webview) => webview
|
||||
.delegate()
|
||||
.load_web_resource(webview, web_resource_load),
|
||||
None => self.delegate().load_web_resource(web_resource_load),
|
||||
}
|
||||
},
|
||||
EmbedderMsg::Panic(webview_id, reason, backtrace) => {
|
||||
if let Some(webview) = self.get_webview_handle(webview_id) {
|
||||
|
|
|
@ -2,11 +2,8 @@
|
|||
* 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 embedder_traits::{WebResourceRequest, WebResourceResponseMsg};
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
|
||||
use crate::webview_delegate::AllowOrDenyRequest;
|
||||
use crate::{Servo, WebView};
|
||||
use crate::webview_delegate::{AllowOrDenyRequest, WebResourceLoad};
|
||||
use crate::Servo;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq)]
|
||||
pub enum ServoError {
|
||||
|
@ -27,20 +24,14 @@ pub trait ServoDelegate {
|
|||
/// Request a DevTools connection from a DevTools client. Typically an embedder application
|
||||
/// will show a permissions prompt when this happens to confirm a connection is allowed.
|
||||
fn request_devtools_connection(&self, _servo: &Servo, _request: AllowOrDenyRequest) {}
|
||||
/// Potentially intercept a resource request. If not handled, the request will not be intercepted.
|
||||
/// Triggered when Servo will load a web (HTTP/HTTPS) resource. The load may be
|
||||
/// intercepted and alternate contents can be loaded by the client by calling
|
||||
/// [`WebResourceLoad::intercept`]. If not handled, the load will continue as normal.
|
||||
///
|
||||
/// Note: If this request is associated with a `WebView`, the `WebViewDelegate` will
|
||||
/// receive this notification first and have a chance to intercept the request.
|
||||
///
|
||||
/// TODO: This API needs to be reworked to match the new model of how responses are sent.
|
||||
fn intercept_web_resource_load(
|
||||
&self,
|
||||
_webview: Option<WebView>,
|
||||
_request: &WebResourceRequest,
|
||||
response_sender: IpcSender<WebResourceResponseMsg>,
|
||||
) {
|
||||
let _ = response_sender.send(WebResourceResponseMsg::None);
|
||||
}
|
||||
/// Note: This delegate method is called for all resource loads not associated with a
|
||||
/// [`WebView`]. For loads associated with a [`WebView`], Servo will call
|
||||
/// [`crate::WebViewDelegate::load_web_resource`].
|
||||
fn load_web_resource(&self, _load: WebResourceLoad) {}
|
||||
}
|
||||
|
||||
pub(crate) struct DefaultServoDelegate;
|
||||
|
|
|
@ -9,7 +9,8 @@ use compositing_traits::ConstellationMsg;
|
|||
use embedder_traits::{
|
||||
AllowOrDeny, AuthenticationResponse, ContextMenuResult, Cursor, FilterPattern,
|
||||
GamepadHapticEffectType, InputMethodType, LoadStatus, MediaSessionEvent, PermissionFeature,
|
||||
PromptDefinition, PromptOrigin, WebResourceRequest, WebResourceResponseMsg,
|
||||
PromptDefinition, PromptOrigin, WebResourceRequest, WebResourceResponse,
|
||||
WebResourceResponseMsg,
|
||||
};
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
use keyboard_types::KeyboardEvent;
|
||||
|
@ -143,6 +144,90 @@ impl Drop for AuthenticationRequest {
|
|||
}
|
||||
}
|
||||
|
||||
/// Information related to the loading of a web resource. These are created for all HTTP requests.
|
||||
/// The client may choose to intercept the load of web resources and send an alternate response
|
||||
/// by calling [`WebResourceLoad::intercept`].
|
||||
pub struct WebResourceLoad {
|
||||
pub request: WebResourceRequest,
|
||||
pub(crate) response_sender: IpcSender<WebResourceResponseMsg>,
|
||||
pub(crate) intercepted: bool,
|
||||
}
|
||||
|
||||
impl WebResourceLoad {
|
||||
/// The [`WebResourceRequest`] associated with this [`WebResourceLoad`].
|
||||
pub fn request(&self) -> &WebResourceRequest {
|
||||
&self.request
|
||||
}
|
||||
/// Intercept this [`WebResourceLoad`] and control the response via the returned
|
||||
/// [`InterceptedWebResourceLoad`].
|
||||
pub fn intercept(mut self, response: WebResourceResponse) -> InterceptedWebResourceLoad {
|
||||
let _ = self
|
||||
.response_sender
|
||||
.send(WebResourceResponseMsg::Start(response));
|
||||
self.intercepted = true;
|
||||
InterceptedWebResourceLoad {
|
||||
request: self.request.clone(),
|
||||
response_sender: self.response_sender.clone(),
|
||||
finished: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for WebResourceLoad {
|
||||
fn drop(&mut self) {
|
||||
if !self.intercepted {
|
||||
let _ = self
|
||||
.response_sender
|
||||
.send(WebResourceResponseMsg::DoNotIntercept);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An intercepted web resource load. This struct allows the client to send an alternative response
|
||||
/// after calling [`WebResourceLoad::intercept`]. In order to send chunks of body data, the client
|
||||
/// must call [`InterceptedWebResourceLoad::send_body_data`]. When the interception is complete, the client
|
||||
/// should call [`InterceptedWebResourceLoad::finish`]. If neither `finish()` or `cancel()` are called,
|
||||
/// this interception will automatically be finished when dropped.
|
||||
pub struct InterceptedWebResourceLoad {
|
||||
pub request: WebResourceRequest,
|
||||
pub(crate) response_sender: IpcSender<WebResourceResponseMsg>,
|
||||
pub(crate) finished: bool,
|
||||
}
|
||||
|
||||
impl InterceptedWebResourceLoad {
|
||||
/// Send a chunk of response body data. It's possible to make subsequent calls to
|
||||
/// this method when streaming body data.
|
||||
pub fn send_body_data(&self, data: Vec<u8>) {
|
||||
let _ = self
|
||||
.response_sender
|
||||
.send(WebResourceResponseMsg::SendBodyData(data));
|
||||
}
|
||||
/// Finish this [`InterceptedWebResourceLoad`] and complete the response.
|
||||
pub fn finish(mut self) {
|
||||
let _ = self
|
||||
.response_sender
|
||||
.send(WebResourceResponseMsg::FinishLoad);
|
||||
self.finished = true;
|
||||
}
|
||||
/// Cancel this [`InterceptedWebResourceLoad`], which will trigger a network error.
|
||||
pub fn cancel(mut self) {
|
||||
let _ = self
|
||||
.response_sender
|
||||
.send(WebResourceResponseMsg::CancelLoad);
|
||||
self.finished = true;
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for InterceptedWebResourceLoad {
|
||||
fn drop(&mut self) {
|
||||
if !self.finished {
|
||||
let _ = self
|
||||
.response_sender
|
||||
.send(WebResourceResponseMsg::FinishLoad);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WebViewDelegate {
|
||||
/// The URL of the currently loaded page in this [`WebView`] has changed. The new
|
||||
/// URL can accessed via [`WebView::url`].
|
||||
|
@ -296,19 +381,14 @@ pub trait WebViewDelegate {
|
|||
/// Request to stop a haptic effect on a connected gamepad.
|
||||
fn stop_gamepad_haptic_effect(&self, _webview: WebView, _: usize, _: IpcSender<bool>) {}
|
||||
|
||||
/// Potentially intercept a resource request. If not handled, the request will not be intercepted.
|
||||
/// Triggered when this [`WebView`] will load a web (HTTP/HTTPS) resource. The load may be
|
||||
/// intercepted and alternate contents can be loaded by the client by calling
|
||||
/// [`WebResourceLoad::intercept`]. If not handled, the load will continue as normal.
|
||||
///
|
||||
/// Note: The `ServoDelegate` will also receive this notification and have a chance to intercept
|
||||
/// the request.
|
||||
///
|
||||
/// TODO: This API needs to be reworked to match the new model of how responses are sent.
|
||||
fn intercept_web_resource_load(
|
||||
&self,
|
||||
_webview: WebView,
|
||||
_request: &WebResourceRequest,
|
||||
_response_sender: IpcSender<WebResourceResponseMsg>,
|
||||
) {
|
||||
}
|
||||
/// Note: This delegate method is called for all resource loads associated with a [`WebView`].
|
||||
/// For loads not associated with a [`WebView`], such as those for service workers, Servo
|
||||
/// will call [`crate::ServoDelegate::load_web_resource`].
|
||||
fn load_web_resource(&self, _webview: WebView, _load: WebResourceLoad) {}
|
||||
}
|
||||
|
||||
pub(crate) struct DefaultWebViewDelegate;
|
||||
|
|
|
@ -30,5 +30,6 @@ malloc_size_of_derive = { workspace = true }
|
|||
num-traits = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
servo_url = { path = "../../url" }
|
||||
url = { workspace = true }
|
||||
webrender_api = { workspace = true }
|
||||
webxr-api = { workspace = true, features = ["ipc"], optional = true }
|
||||
|
|
|
@ -18,6 +18,7 @@ use malloc_size_of_derive::MallocSizeOf;
|
|||
use num_derive::FromPrimitive;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use servo_url::ServoUrl;
|
||||
use url::Url;
|
||||
use webrender_api::units::{DeviceIntPoint, DeviceIntRect, DeviceIntSize};
|
||||
|
||||
pub use crate::input_events::*;
|
||||
|
@ -452,49 +453,30 @@ pub struct WebResourceRequest {
|
|||
)]
|
||||
#[ignore_malloc_size_of = "Defined in hyper"]
|
||||
pub headers: HeaderMap,
|
||||
pub url: ServoUrl,
|
||||
pub url: Url,
|
||||
pub is_for_main_frame: bool,
|
||||
pub is_redirect: bool,
|
||||
}
|
||||
|
||||
impl WebResourceRequest {
|
||||
pub fn new(
|
||||
method: Method,
|
||||
headers: HeaderMap,
|
||||
url: ServoUrl,
|
||||
is_for_main_frame: bool,
|
||||
is_redirect: bool,
|
||||
) -> Self {
|
||||
WebResourceRequest {
|
||||
method,
|
||||
url,
|
||||
headers,
|
||||
is_for_main_frame,
|
||||
is_redirect,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
pub enum WebResourceResponseMsg {
|
||||
// Response of WebResourceRequest, no body included.
|
||||
/// Start an interception of this web resource load. It's expected that the client subsequently
|
||||
/// send either a `CancelLoad` or `FinishLoad` message after optionally sending chunks of body
|
||||
/// data via `SendBodyData`.
|
||||
Start(WebResourceResponse),
|
||||
// send a body chunk. It is expected Response sent before body.
|
||||
Body(HttpBodyData),
|
||||
// not to override the response.
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
pub enum HttpBodyData {
|
||||
Chunk(Vec<u8>),
|
||||
Done,
|
||||
Cancelled,
|
||||
/// Send a chunk of body data.
|
||||
SendBodyData(Vec<u8>),
|
||||
/// Signal that this load has been finished by the interceptor.
|
||||
FinishLoad,
|
||||
/// Signal that this load has been cancelled by the interceptor.
|
||||
CancelLoad,
|
||||
/// Signal that this load will not be intercepted.
|
||||
DoNotIntercept,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
|
||||
pub struct WebResourceResponse {
|
||||
pub url: ServoUrl,
|
||||
pub url: Url,
|
||||
#[serde(
|
||||
deserialize_with = "::hyper_serde::deserialize",
|
||||
serialize_with = "::hyper_serde::serialize"
|
||||
|
@ -511,7 +493,7 @@ pub struct WebResourceResponse {
|
|||
}
|
||||
|
||||
impl WebResourceResponse {
|
||||
pub fn new(url: ServoUrl) -> WebResourceResponse {
|
||||
pub fn new(url: Url) -> WebResourceResponse {
|
||||
WebResourceResponse {
|
||||
url,
|
||||
headers: HeaderMap::new(),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue