mirror of
https://github.com/servo/servo
synced 2026-04-25 17:15:48 +02:00
Force callers to claim blob url before making a fetch request (#43746)
`blob` URLs have a implicit blob URL entry attached, which stores the data contained in the blob. The specification requires this entry to be resolved as the URL is parsed. We only resolve it inside `net` when loading the URL. That causes problems if the blob entry has been revoked in the meantime - see https://github.com/servo/servo/issues/25226. Ideally we would want to resolve blobs at parse-time as required. But because `ServoUrl` is such a fundamental type, I've not managed to do this change without having to touch hundreds of files at once. Thus, we now require passing a `UrlWithBlobClaim` instead of a `ServoUrl` when `fetch`-ing. This type proves that the caller has acquired the blob beforehand. As a temporary escape hatch, I've added `UrlWithBlobClaim::from_url_without_having_claimed_blob`. That method logs a warning if its used unsafely. This method is currently used in most places to keep this change small. Only workers now acquire the blob beforehand. Testing: A new test starts to pass Part of https://github.com/servo/servo/issues/43326 Part of https://github.com/servo/servo/issues/25226 --------- Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> Co-authored-by: Josh Matthews <josh@joshmatthews.net>
This commit is contained in:
@@ -16,6 +16,7 @@ use fonts_traits::{
|
||||
};
|
||||
use log::{debug, trace};
|
||||
use malloc_size_of_derive::MallocSizeOf;
|
||||
use net_traits::blob_url_store::UrlWithBlobClaim;
|
||||
use net_traits::policy_container::PolicyContainer;
|
||||
use net_traits::request::{
|
||||
CredentialsMode, Destination, InsecureRequestsPolicy, Referrer, RequestBuilder, RequestClient,
|
||||
@@ -986,7 +987,7 @@ impl RemoteWebFontDownloader {
|
||||
|
||||
let request = RequestBuilder::new(
|
||||
state.webview_id,
|
||||
url.clone().into(),
|
||||
UrlWithBlobClaim::from_url_without_having_claimed_blob(url.clone().into()),
|
||||
Referrer::ReferrerUrl(document_context.document_url.clone()),
|
||||
)
|
||||
.destination(Destination::Font)
|
||||
|
||||
@@ -540,12 +540,12 @@ pub async fn main_fetch(
|
||||
.await;
|
||||
|
||||
let mut response = match response {
|
||||
Some(res) => res,
|
||||
Some(response) => response,
|
||||
None => {
|
||||
// Step 12. If response is null, then set response to the result
|
||||
// of running the steps corresponding to the first matching statement:
|
||||
let same_origin = if let Origin::Origin(ref origin) = request.origin {
|
||||
*origin == current_url.origin()
|
||||
*origin == request.current_url_with_blob_claim().origin()
|
||||
} else {
|
||||
false
|
||||
};
|
||||
@@ -726,7 +726,11 @@ pub async fn main_fetch(
|
||||
|
||||
// Step 16. If internalResponse’s URL list is empty, then set it to a clone of request’s URL list.
|
||||
if internal_response.url_list.is_empty() {
|
||||
internal_response.url_list.clone_from(&request.url_list)
|
||||
internal_response.url_list = request
|
||||
.url_list
|
||||
.iter()
|
||||
.map(|locked_url| locked_url.url())
|
||||
.collect();
|
||||
}
|
||||
|
||||
// Step 17. Set internalResponse’s redirect taint to request’s redirect-taint.
|
||||
@@ -1056,18 +1060,22 @@ async fn scheme_fetch(
|
||||
|
||||
// Step 2: Let request be fetchParams’s request.
|
||||
let request = &mut fetch_params.request;
|
||||
let url = request.current_url();
|
||||
let url_and_blob_lock = request.current_url_with_blob_claim();
|
||||
|
||||
let scheme = url.scheme();
|
||||
let scheme = url_and_blob_lock.scheme();
|
||||
match scheme {
|
||||
"about" if url.path() == "blank" => create_blank_reply(url, request.timing_type()),
|
||||
"about" if url.path() == "memory" => create_about_memory(url, request.timing_type()),
|
||||
"about" if url_and_blob_lock.path() == "blank" => {
|
||||
create_blank_reply(url_and_blob_lock.url(), request.timing_type())
|
||||
},
|
||||
"about" if url_and_blob_lock.path() == "memory" => {
|
||||
create_about_memory(url_and_blob_lock.url(), request.timing_type())
|
||||
},
|
||||
|
||||
"chrome" if url.path() == "allowcert" => {
|
||||
"chrome" if url_and_blob_lock.path() == "allowcert" => {
|
||||
if let Err(error) = handle_allowcert_request(request, context) {
|
||||
warn!("Could not handle allowcert request: {error}");
|
||||
}
|
||||
create_blank_reply(url, request.timing_type())
|
||||
create_blank_reply(url_and_blob_lock.url(), request.timing_type())
|
||||
},
|
||||
|
||||
"http" | "https" => {
|
||||
|
||||
@@ -18,10 +18,10 @@ use http::header::{self, HeaderValue};
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
use log::warn;
|
||||
use mime::{self, Mime};
|
||||
use net_traits::blob_url_store::{BlobBuf, BlobURLStoreError};
|
||||
use net_traits::blob_url_store::{BlobBuf, BlobTokenCommunicator, BlobURLStoreError};
|
||||
use net_traits::filemanager_thread::{
|
||||
FileManagerResult, FileManagerThreadError, FileManagerThreadMsg, FileTokenCheck,
|
||||
ReadFileProgress, RelativePos,
|
||||
GetTokenForFileReply, ReadFileProgress, RelativePos,
|
||||
};
|
||||
use net_traits::http_percent_encode;
|
||||
use net_traits::response::{Response, ResponseBody};
|
||||
@@ -84,13 +84,18 @@ enum FileImpl {
|
||||
pub struct FileManager {
|
||||
embedder_proxy: GenericEmbedderProxy<NetToEmbedderMsg>,
|
||||
store: Arc<FileManagerStore>,
|
||||
blob_token_communicator: Arc<Mutex<BlobTokenCommunicator>>,
|
||||
}
|
||||
|
||||
impl FileManager {
|
||||
pub fn new(embedder_proxy: GenericEmbedderProxy<NetToEmbedderMsg>) -> FileManager {
|
||||
pub fn new(
|
||||
embedder_proxy: GenericEmbedderProxy<NetToEmbedderMsg>,
|
||||
blob_token_communicator: Arc<Mutex<BlobTokenCommunicator>>,
|
||||
) -> FileManager {
|
||||
FileManager {
|
||||
embedder_proxy,
|
||||
store: Arc::new(FileManagerStore::new()),
|
||||
blob_token_communicator,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,8 +113,8 @@ impl FileManager {
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn get_token_for_file(&self, file_id: &Uuid) -> FileTokenCheck {
|
||||
self.store.get_token_for_file(file_id)
|
||||
pub(crate) fn get_token_for_file(&self, file_id: &Uuid, allow_revoked: bool) -> FileTokenCheck {
|
||||
self.store.get_token_for_file(file_id, allow_revoked)
|
||||
}
|
||||
|
||||
pub(crate) fn invalidate_token(&self, token: &FileTokenCheck, file_id: &Uuid) {
|
||||
@@ -182,6 +187,22 @@ impl FileManager {
|
||||
FileManagerThreadMsg::ActivateBlobURL(id, sender, origin) => {
|
||||
let _ = sender.send(self.store.set_blob_url_validity(true, &id, &origin));
|
||||
},
|
||||
FileManagerThreadMsg::GetTokenForFile(id, _origin, sender) => {
|
||||
let token = match self.get_token_for_file(&id, false) {
|
||||
FileTokenCheck::Required(token) => Some(token),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let communicator = self.blob_token_communicator.lock();
|
||||
let _ = sender.send(GetTokenForFileReply {
|
||||
token,
|
||||
revoke_sender: communicator.revoke_sender.clone(),
|
||||
refresh_sender: communicator.refresh_token_sender.clone(),
|
||||
});
|
||||
},
|
||||
FileManagerThreadMsg::RevokeTokenForFile(token, id) => {
|
||||
self.invalidate_token(&FileTokenCheck::Required(token), &id);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,7 +480,7 @@ impl FileManagerStore {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_token_for_file(&self, file_id: &Uuid) -> FileTokenCheck {
|
||||
pub(crate) fn get_token_for_file(&self, file_id: &Uuid, allow_revoked: bool) -> FileTokenCheck {
|
||||
let mut entries = self.entries.write();
|
||||
let parent_id = match entries.get(file_id) {
|
||||
Some(entry) => {
|
||||
@@ -471,12 +492,11 @@ impl FileManagerStore {
|
||||
},
|
||||
None => return FileTokenCheck::ShouldFail,
|
||||
};
|
||||
let file_id = match parent_id.as_ref() {
|
||||
Some(id) => id,
|
||||
None => file_id,
|
||||
};
|
||||
let file_id = parent_id.as_ref().unwrap_or(file_id);
|
||||
|
||||
if let Some(entry) = entries.get_mut(file_id) {
|
||||
if !entry.is_valid_url.load(Ordering::Acquire) {
|
||||
if !allow_revoked && !entry.is_valid_url.load(Ordering::Acquire) {
|
||||
log::warn!("Refusing to grant token for revoked blob url: {file_id:?}");
|
||||
return FileTokenCheck::ShouldFail;
|
||||
}
|
||||
let token = Uuid::new_v4();
|
||||
|
||||
@@ -40,6 +40,7 @@ use ipc_channel::ipc::{self, IpcSender};
|
||||
use ipc_channel::router::ROUTER;
|
||||
use log::{debug, error, info, log_enabled, warn};
|
||||
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
||||
use net_traits::blob_url_store::UrlWithBlobClaim;
|
||||
use net_traits::fetch::headers::get_value_from_header_list;
|
||||
use net_traits::http_status::HttpStatus;
|
||||
use net_traits::policy_container::RequestPolicyContainer;
|
||||
@@ -1368,7 +1369,11 @@ pub async fn http_redirect_fetch(
|
||||
// Steps 15-17 relate to timing, which is not implemented 1:1 with the spec.
|
||||
|
||||
// Step 18: Append locationURL to request’s URL list.
|
||||
request.url_list.push(location_url);
|
||||
request
|
||||
.url_list
|
||||
.push(UrlWithBlobClaim::from_url_without_having_claimed_blob(
|
||||
location_url,
|
||||
));
|
||||
|
||||
// Step 19: Invoke set request’s referrer policy on redirect on request and internalResponse.
|
||||
set_requests_referrer_policy_on_redirect(request, response.actual_response());
|
||||
@@ -1768,7 +1773,11 @@ async fn http_network_or_cache_fetch(
|
||||
let mut response = response.unwrap();
|
||||
|
||||
// Step 11. Set response’s URL list to a clone of httpRequest’s URL list.
|
||||
response.url_list = http_request.url_list.clone();
|
||||
response.url_list = http_request
|
||||
.url_list
|
||||
.iter()
|
||||
.map(|claimed_url| claimed_url.url())
|
||||
.collect();
|
||||
|
||||
// Step 12. If httpRequest’s header list contains `Range`, then set response’s range-requested flag.
|
||||
if http_request.headers.contains_key(RANGE) {
|
||||
@@ -2450,7 +2459,7 @@ async fn cors_preflight_fetch(
|
||||
// referrer policy, mode is "cors", and response tainting is "cors".
|
||||
let mut preflight = RequestBuilder::new(
|
||||
request.target_webview_id,
|
||||
request.current_url(),
|
||||
request.current_url_with_blob_claim(),
|
||||
request.referrer.clone(),
|
||||
)
|
||||
.method(Method::OPTIONS)
|
||||
|
||||
@@ -28,8 +28,8 @@ impl ProtocolHandler for BlobProtocolHander {
|
||||
done_chan: &mut DoneChannel,
|
||||
context: &FetchContext,
|
||||
) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||
let url = request.current_url();
|
||||
debug!("Loading blob {}", url.as_str());
|
||||
let url_and_blob_claim = request.current_url_with_blob_claim();
|
||||
debug!("Loading blob {}", url_and_blob_claim.as_str());
|
||||
|
||||
// Step 2.
|
||||
if request.method != Method::GET {
|
||||
@@ -39,16 +39,22 @@ impl ProtocolHandler for BlobProtocolHander {
|
||||
let range_header = request.headers.typed_get::<Range>();
|
||||
let is_range_request = range_header.is_some();
|
||||
|
||||
let (id, origin) = match parse_blob_url(&url) {
|
||||
Ok((id, origin)) => (id, origin),
|
||||
Err(error) => {
|
||||
let (file_id, origin) = if let Some(token) = url_and_blob_claim.token() {
|
||||
(token.file_id, token.origin.clone())
|
||||
} else {
|
||||
// FIXME: This should never happen, we should have acquired a token beforehand
|
||||
let Ok((id, _)) = parse_blob_url(&url_and_blob_claim.url()) else {
|
||||
return Box::pin(ready(Response::network_error(
|
||||
NetworkError::ResourceLoadError(format!("Invalid blob URL ({error})")),
|
||||
NetworkError::ResourceLoadError("Invalid blob URL".into()),
|
||||
)));
|
||||
},
|
||||
};
|
||||
(id, url_and_blob_claim.url().origin())
|
||||
};
|
||||
|
||||
let mut response = Response::new(url, ResourceFetchTiming::new(request.timing_type()));
|
||||
let mut response = Response::new(
|
||||
url_and_blob_claim.url(),
|
||||
ResourceFetchTiming::new(request.timing_type()),
|
||||
);
|
||||
response.status = HttpStatus::default();
|
||||
|
||||
if is_range_request {
|
||||
@@ -63,7 +69,7 @@ impl ProtocolHandler for BlobProtocolHander {
|
||||
if let Err(err) = context.filemanager.fetch_file(
|
||||
&mut done_sender,
|
||||
context.cancellation_listener.clone(),
|
||||
id,
|
||||
file_id,
|
||||
&context.file_token,
|
||||
origin,
|
||||
&mut response,
|
||||
|
||||
@@ -11,6 +11,7 @@ use std::pin::Pin;
|
||||
use headers::Range;
|
||||
use http::StatusCode;
|
||||
use log::error;
|
||||
use net_traits::blob_url_store::UrlWithBlobClaim;
|
||||
use net_traits::filemanager_thread::RelativePos;
|
||||
use net_traits::request::Request;
|
||||
use net_traits::response::Response;
|
||||
@@ -219,7 +220,9 @@ impl ProtocolHandler for WebPageContentProtocolHandler {
|
||||
// Ensure we did a proper substitution with a HTTP result
|
||||
assert!(matches!(result_url.scheme(), "http" | "https"));
|
||||
// Step 9. Navigate an appropriate navigable to resultURL.
|
||||
request.url_list.push(result_url);
|
||||
request
|
||||
.url_list
|
||||
.push(UrlWithBlobClaim::new(result_url, None));
|
||||
let request2 = request.clone();
|
||||
let context2 = context.clone();
|
||||
Box::pin(async move { fetch(request2, &mut DiscardFetch, &context2).await })
|
||||
|
||||
@@ -19,7 +19,7 @@ use embedder_traits::GenericEmbedderProxy;
|
||||
use hyper_serde::Serde;
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
use log::{debug, trace, warn};
|
||||
use net_traits::blob_url_store::parse_blob_url;
|
||||
use net_traits::blob_url_store::{BlobTokenCommunicator, parse_blob_url};
|
||||
use net_traits::filemanager_thread::FileTokenCheck;
|
||||
use net_traits::pub_domains::public_suffix_list_size_of;
|
||||
use net_traits::request::{Destination, PreloadEntry, PreloadId, RequestBuilder, RequestId};
|
||||
@@ -140,7 +140,13 @@ pub fn new_core_resource_thread(
|
||||
let (public_setup_chan, public_setup_port) = generic_channel::channel().unwrap();
|
||||
let (private_setup_chan, private_setup_port) = generic_channel::channel().unwrap();
|
||||
let (report_chan, report_port) = generic_channel::channel().unwrap();
|
||||
let (revoke_sender, revoke_receiver) = generic_channel::channel().unwrap();
|
||||
let (refresh_sender, refresh_receiver) = generic_channel::channel().unwrap();
|
||||
|
||||
let blob_token_communicator = Arc::new(Mutex::new(BlobTokenCommunicator {
|
||||
revoke_sender,
|
||||
refresh_token_sender: refresh_sender,
|
||||
}));
|
||||
thread::Builder::new()
|
||||
.name("ResourceManager".to_owned())
|
||||
.spawn(move || {
|
||||
@@ -150,6 +156,7 @@ pub fn new_core_resource_thread(
|
||||
embedder_proxy.clone(),
|
||||
ca_certificates.clone(),
|
||||
ignore_certificate_errors,
|
||||
blob_token_communicator,
|
||||
);
|
||||
|
||||
let mut channel_manager = ResourceChannelManager {
|
||||
@@ -167,6 +174,8 @@ pub fn new_core_resource_thread(
|
||||
public_setup_port,
|
||||
private_setup_port,
|
||||
report_port,
|
||||
revoke_receiver,
|
||||
refresh_receiver,
|
||||
protocols,
|
||||
embedder_proxy,
|
||||
)
|
||||
@@ -241,11 +250,14 @@ fn create_http_states(
|
||||
}
|
||||
|
||||
impl ResourceChannelManager {
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
fn start(
|
||||
&mut self,
|
||||
public_receiver: GenericReceiver<CoreResourceMsg>,
|
||||
private_receiver: GenericReceiver<CoreResourceMsg>,
|
||||
memory_reporter: GenericReceiver<CoreResourceMsg>,
|
||||
revoke_receiver: GenericReceiver<CoreResourceMsg>,
|
||||
refresh_receiver: GenericReceiver<CoreResourceMsg>,
|
||||
protocols: Arc<ProtocolRegistry>,
|
||||
embedder_proxy: GenericEmbedderProxy<NetToEmbedderMsg>,
|
||||
) {
|
||||
@@ -260,6 +272,8 @@ impl ResourceChannelManager {
|
||||
let private_id = rx_set.add(private_receiver);
|
||||
let public_id = rx_set.add(public_receiver);
|
||||
let reporter_id = rx_set.add(memory_reporter);
|
||||
let revoker_id = rx_set.add(revoke_receiver);
|
||||
let refresh_id = rx_set.add(refresh_receiver);
|
||||
|
||||
loop {
|
||||
for received in rx_set.select().into_iter() {
|
||||
@@ -270,7 +284,31 @@ impl ResourceChannelManager {
|
||||
log::error!("Found selection error: {error}")
|
||||
},
|
||||
GenericSelectionResult::MessageReceived(id, msg) => {
|
||||
if id == reporter_id {
|
||||
if id == revoker_id {
|
||||
let CoreResourceMsg::RevokeTokenForFile(revocation_request) = msg
|
||||
else {
|
||||
log::error!("Blob revocation channel received unexpected message");
|
||||
continue;
|
||||
};
|
||||
self.resource_manager.filemanager.invalidate_token(
|
||||
&FileTokenCheck::Required(revocation_request.token),
|
||||
&revocation_request.blob_id,
|
||||
)
|
||||
} else if id == refresh_id {
|
||||
let CoreResourceMsg::RefreshTokenForFile(refresh_request) = msg else {
|
||||
log::error!("Blob revocation channel received unexpected message");
|
||||
continue;
|
||||
};
|
||||
|
||||
let FileTokenCheck::Required(refreshed_token) = self
|
||||
.resource_manager
|
||||
.filemanager
|
||||
.get_token_for_file(&refresh_request.blob_id, true)
|
||||
else {
|
||||
unreachable!();
|
||||
};
|
||||
let _ = refresh_request.new_token_sender.send(refreshed_token);
|
||||
} else if id == reporter_id {
|
||||
if let CoreResourceMsg::CollectMemoryReport(report_chan) = msg {
|
||||
self.process_report(
|
||||
report_chan,
|
||||
@@ -607,8 +645,10 @@ impl ResourceChannelManager {
|
||||
let _ = sender.send(());
|
||||
return false;
|
||||
},
|
||||
// Ignore this message as we handle it only in the reporter chan
|
||||
CoreResourceMsg::CollectMemoryReport(_) => {},
|
||||
// Ignore these messages as they are only sent on very specific channels.
|
||||
CoreResourceMsg::CollectMemoryReport(_) |
|
||||
CoreResourceMsg::RevokeTokenForFile(..) |
|
||||
CoreResourceMsg::RefreshTokenForFile(..) => {},
|
||||
}
|
||||
true
|
||||
}
|
||||
@@ -654,11 +694,12 @@ impl CoreResourceManager {
|
||||
embedder_proxy: GenericEmbedderProxy<NetToEmbedderMsg>,
|
||||
ca_certificates: CACertificates<'static>,
|
||||
ignore_certificate_errors: bool,
|
||||
blob_token_communicator: Arc<Mutex<BlobTokenCommunicator>>,
|
||||
) -> CoreResourceManager {
|
||||
CoreResourceManager {
|
||||
devtools_sender,
|
||||
sw_managers: Default::default(),
|
||||
filemanager: FileManager::new(embedder_proxy.clone()),
|
||||
filemanager: FileManager::new(embedder_proxy.clone(), blob_token_communicator),
|
||||
request_interceptor: RequestInterceptor::new(embedder_proxy),
|
||||
ca_certificates,
|
||||
ignore_certificate_errors,
|
||||
@@ -717,17 +758,18 @@ impl CoreResourceManager {
|
||||
// In the case of a valid blob URL, acquiring a token granting access to a file,
|
||||
// regardless if the URL is revoked after token acquisition.
|
||||
//
|
||||
// TODO: to make more tests pass, acquire this token earlier,
|
||||
// probably in a separate message flow.
|
||||
//
|
||||
// In such a setup, the token would not be acquired here,
|
||||
// but could instead be contained in the actual CoreResourceMsg::Fetch message.
|
||||
//
|
||||
// See https://github.com/servo/servo/issues/25226
|
||||
// Ideally all callers should have claimed the blob entry themselves, but we're not there
|
||||
// yet.
|
||||
let (file_token, blob_url_file_id) = match url.scheme() {
|
||||
"blob" => {
|
||||
if let Ok((id, _)) = parse_blob_url(&url) {
|
||||
(self.filemanager.get_token_for_file(&id), Some(id))
|
||||
if let Some(token) = request.current_url_with_blob_claim().token() {
|
||||
(FileTokenCheck::Required(token.token), Some(token.file_id))
|
||||
} else if let Ok((id, _)) = parse_blob_url(&url) {
|
||||
// See https://github.com/servo/servo/issues/25226
|
||||
log::warn!(
|
||||
"Failed to claim blob URL entry of valid blob URL before passing it to `net`. This causes race conditions."
|
||||
);
|
||||
(self.filemanager.get_token_for_file(&id, false), Some(id))
|
||||
} else {
|
||||
(FileTokenCheck::ShouldFail, None)
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ use hyper::service::service_fn;
|
||||
use hyper::{Request as HyperRequest, Response as HyperResponse};
|
||||
use hyper_util::rt::tokio::TokioIo;
|
||||
use net_traits::AsyncRuntime;
|
||||
use net_traits::blob_url_store::UrlWithBlobClaim;
|
||||
use rustls_pki_types::pem::PemObject;
|
||||
use rustls_pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer};
|
||||
use servo_default_resources as _;
|
||||
@@ -79,7 +80,7 @@ impl Server {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_server<H>(handler: H) -> (Server, ServoUrl)
|
||||
pub fn make_server<H>(handler: H) -> (Server, UrlWithBlobClaim)
|
||||
where
|
||||
H: Fn(HyperRequest<Incoming>, &mut HyperResponse<BoxBody<Bytes, hyper::Error>>)
|
||||
+ Send
|
||||
@@ -99,7 +100,7 @@ where
|
||||
);
|
||||
|
||||
let url_string = format!("http://localhost:{}", listener.local_addr().unwrap().port());
|
||||
let url = ServoUrl::parse(&url_string).unwrap();
|
||||
let url = UrlWithBlobClaim::new(ServoUrl::parse(&url_string).unwrap(), None);
|
||||
|
||||
let graceful = hyper_util::server::graceful::GracefulShutdown::new();
|
||||
|
||||
@@ -175,7 +176,7 @@ fn load_private_key_from_file(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_ssl_server<H>(handler: H) -> (Server, ServoUrl)
|
||||
pub fn make_ssl_server<H>(handler: H) -> (Server, UrlWithBlobClaim)
|
||||
where
|
||||
H: Fn(HyperRequest<Incoming>, &mut HyperResponse<BoxBody<Bytes, hyper::Error>>)
|
||||
+ Send
|
||||
@@ -194,7 +195,7 @@ where
|
||||
);
|
||||
|
||||
let url_string = format!("http://localhost:{}", listener.local_addr().unwrap().port());
|
||||
let url = ServoUrl::parse(&url_string).unwrap();
|
||||
let url = UrlWithBlobClaim::new(ServoUrl::parse(&url_string).unwrap(), None);
|
||||
|
||||
let cert_path = Path::new("../../resources/self_signed_certificate_for_testing.crt")
|
||||
.canonicalize()
|
||||
|
||||
@@ -7,6 +7,7 @@ use std::ops::Deref;
|
||||
use headers::{ContentType, HeaderMapExt};
|
||||
use hyper_serde::Serde;
|
||||
use mime::{self, Mime};
|
||||
use net_traits::blob_url_store::UrlWithBlobClaim;
|
||||
use net_traits::request::Referrer;
|
||||
use net_traits::response::ResponseBody;
|
||||
use net_traits::{FetchMetadata, FilteredMetadata, NetworkError};
|
||||
@@ -24,7 +25,7 @@ fn assert_parse(
|
||||
) {
|
||||
use net_traits::request::RequestBuilder;
|
||||
|
||||
let url = ServoUrl::parse(url).unwrap();
|
||||
let url = UrlWithBlobClaim::new(ServoUrl::parse(url).unwrap(), None);
|
||||
let request = RequestBuilder::new(Some(TEST_WEBVIEW_ID), url.clone(), Referrer::NoReferrer)
|
||||
.origin(url.origin())
|
||||
.pipeline_id(None)
|
||||
|
||||
@@ -32,6 +32,7 @@ use net::filemanager_thread::FileManager;
|
||||
use net::hsts::HstsEntry;
|
||||
use net::protocols::ProtocolRegistry;
|
||||
use net::request_interceptor::RequestInterceptor;
|
||||
use net_traits::blob_url_store::{BlobTokenCommunicator, UrlWithBlobClaim};
|
||||
use net_traits::filemanager_thread::FileTokenCheck;
|
||||
use net_traits::http_status::HttpStatus;
|
||||
use net_traits::request::{
|
||||
@@ -83,10 +84,14 @@ fn test_fetch_response_is_not_network_error() {
|
||||
#[test]
|
||||
fn test_fetch_on_bad_port_is_network_error() {
|
||||
let url = ServoUrl::parse("http://www.example.org:6667").unwrap();
|
||||
let request = RequestBuilder::new(Some(TEST_WEBVIEW_ID), url.clone(), Referrer::NoReferrer)
|
||||
.origin(url.origin())
|
||||
.policy_container(Default::default())
|
||||
.build();
|
||||
let request = RequestBuilder::new(
|
||||
Some(TEST_WEBVIEW_ID),
|
||||
UrlWithBlobClaim::new(url.clone(), None),
|
||||
Referrer::NoReferrer,
|
||||
)
|
||||
.origin(url.origin())
|
||||
.policy_container(Default::default())
|
||||
.build();
|
||||
let fetch_response = fetch(request, None);
|
||||
assert!(fetch_response.is_network_error());
|
||||
let fetch_error = fetch_response.get_network_error().unwrap();
|
||||
@@ -124,10 +129,14 @@ fn test_fetch_response_body_matches_const_message() {
|
||||
#[test]
|
||||
fn test_fetch_aboutblank() {
|
||||
let url = ServoUrl::parse("about:blank").unwrap();
|
||||
let request = RequestBuilder::new(Some(TEST_WEBVIEW_ID), url.clone(), Referrer::NoReferrer)
|
||||
.origin(url.origin())
|
||||
.policy_container(Default::default())
|
||||
.build();
|
||||
let request = RequestBuilder::new(
|
||||
Some(TEST_WEBVIEW_ID),
|
||||
UrlWithBlobClaim::new(url.clone(), None),
|
||||
Referrer::NoReferrer,
|
||||
)
|
||||
.origin(url.origin())
|
||||
.policy_container(Default::default())
|
||||
.build();
|
||||
|
||||
let fetch_response = fetch(request, None);
|
||||
// We should see an opaque-filtered response.
|
||||
@@ -187,10 +196,14 @@ fn test_fetch_blob() {
|
||||
.promote_memory(id.clone(), blob_buf, true, origin.origin());
|
||||
let url = ServoUrl::parse(&format!("blob:{}{}", origin.as_str(), id.simple())).unwrap();
|
||||
|
||||
let request = RequestBuilder::new(Some(TEST_WEBVIEW_ID), url.clone(), Referrer::NoReferrer)
|
||||
.origin(origin.origin())
|
||||
.policy_container(Default::default())
|
||||
.build();
|
||||
let request = RequestBuilder::new(
|
||||
Some(TEST_WEBVIEW_ID),
|
||||
UrlWithBlobClaim::from_url_without_having_claimed_blob(url.clone()),
|
||||
Referrer::NoReferrer,
|
||||
)
|
||||
.origin(origin.origin())
|
||||
.policy_container(Default::default())
|
||||
.build();
|
||||
|
||||
let (sender, receiver) = unbounded();
|
||||
|
||||
@@ -227,10 +240,14 @@ fn test_file() {
|
||||
.unwrap();
|
||||
let url = ServoUrl::from_file_path(path.clone()).unwrap();
|
||||
|
||||
let request = RequestBuilder::new(Some(TEST_WEBVIEW_ID), url.clone(), Referrer::NoReferrer)
|
||||
.origin(url.origin())
|
||||
.policy_container(Default::default())
|
||||
.build();
|
||||
let request = RequestBuilder::new(
|
||||
Some(TEST_WEBVIEW_ID),
|
||||
UrlWithBlobClaim::new(url.clone(), None),
|
||||
Referrer::NoReferrer,
|
||||
)
|
||||
.origin(url.origin())
|
||||
.policy_container(Default::default())
|
||||
.build();
|
||||
|
||||
let mut context = new_fetch_context(None, None);
|
||||
let fetch_response = fetch_with_context(request, &mut context);
|
||||
@@ -269,10 +286,14 @@ fn test_file() {
|
||||
#[test]
|
||||
fn test_fetch_ftp() {
|
||||
let url = ServoUrl::parse("ftp://not-supported").unwrap();
|
||||
let request = RequestBuilder::new(Some(TEST_WEBVIEW_ID), url.clone(), Referrer::NoReferrer)
|
||||
.origin(url.origin())
|
||||
.policy_container(Default::default())
|
||||
.build();
|
||||
let request = RequestBuilder::new(
|
||||
Some(TEST_WEBVIEW_ID),
|
||||
UrlWithBlobClaim::new(url.clone(), None),
|
||||
Referrer::NoReferrer,
|
||||
)
|
||||
.origin(url.origin())
|
||||
.policy_container(Default::default())
|
||||
.build();
|
||||
let fetch_response = fetch(request, None);
|
||||
assert!(fetch_response.is_network_error());
|
||||
}
|
||||
@@ -280,10 +301,14 @@ fn test_fetch_ftp() {
|
||||
#[test]
|
||||
fn test_fetch_bogus_scheme() {
|
||||
let url = ServoUrl::parse("bogus://whatever").unwrap();
|
||||
let request = RequestBuilder::new(Some(TEST_WEBVIEW_ID), url.clone(), Referrer::NoReferrer)
|
||||
.origin(url.origin())
|
||||
.policy_container(Default::default())
|
||||
.build();
|
||||
let request = RequestBuilder::new(
|
||||
Some(TEST_WEBVIEW_ID),
|
||||
UrlWithBlobClaim::new(url.clone(), None),
|
||||
Referrer::NoReferrer,
|
||||
)
|
||||
.origin(url.origin())
|
||||
.policy_container(Default::default())
|
||||
.build();
|
||||
let fetch_response = fetch(request, None);
|
||||
assert!(fetch_response.is_network_error());
|
||||
}
|
||||
@@ -699,7 +724,7 @@ fn test_fetch_with_local_urls_only() {
|
||||
};
|
||||
let (server, server_url) = make_server(handler);
|
||||
|
||||
let do_fetch = |url: ServoUrl| {
|
||||
let do_fetch = |url: UrlWithBlobClaim| {
|
||||
let mut request =
|
||||
RequestBuilder::new(Some(TEST_WEBVIEW_ID), url.clone(), Referrer::NoReferrer)
|
||||
.origin(url.origin())
|
||||
@@ -712,7 +737,7 @@ fn test_fetch_with_local_urls_only() {
|
||||
fetch(request, None)
|
||||
};
|
||||
|
||||
let local_url = ServoUrl::parse("about:blank").unwrap();
|
||||
let local_url = UrlWithBlobClaim::new(ServoUrl::parse("about:blank").unwrap(), None);
|
||||
let local_response = do_fetch(local_url);
|
||||
let server_response = do_fetch(server_url);
|
||||
|
||||
@@ -745,7 +770,10 @@ fn test_fetch_with_hsts() {
|
||||
state: Arc::new(create_http_state(None)),
|
||||
user_agent: DEFAULT_USER_AGENT.into(),
|
||||
devtools_chan: None,
|
||||
filemanager: FileManager::new(embedder_proxy.clone()),
|
||||
filemanager: FileManager::new(
|
||||
embedder_proxy.clone(),
|
||||
BlobTokenCommunicator::stub_for_testing(),
|
||||
),
|
||||
file_token: FileTokenCheck::NotRequired,
|
||||
request_interceptor: Arc::new(TokioMutex::new(RequestInterceptor::new(embedder_proxy))),
|
||||
cancellation_listener: Arc::new(Default::default()),
|
||||
@@ -808,7 +836,10 @@ fn test_load_adds_host_to_hsts_list_when_url_is_https() {
|
||||
state: Arc::new(create_http_state(None)),
|
||||
user_agent: DEFAULT_USER_AGENT.into(),
|
||||
devtools_chan: None,
|
||||
filemanager: FileManager::new(embedder_proxy.clone()),
|
||||
filemanager: FileManager::new(
|
||||
embedder_proxy.clone(),
|
||||
BlobTokenCommunicator::stub_for_testing(),
|
||||
),
|
||||
file_token: FileTokenCheck::NotRequired,
|
||||
request_interceptor: Arc::new(TokioMutex::new(RequestInterceptor::new(embedder_proxy))),
|
||||
cancellation_listener: Arc::new(Default::default()),
|
||||
@@ -876,7 +907,10 @@ fn test_fetch_self_signed() {
|
||||
state: Arc::new(create_http_state(None)),
|
||||
user_agent: DEFAULT_USER_AGENT.into(),
|
||||
devtools_chan: None,
|
||||
filemanager: FileManager::new(embedder_proxy.clone()),
|
||||
filemanager: FileManager::new(
|
||||
embedder_proxy.clone(),
|
||||
BlobTokenCommunicator::stub_for_testing(),
|
||||
),
|
||||
file_token: FileTokenCheck::NotRequired,
|
||||
request_interceptor: Arc::new(TokioMutex::new(RequestInterceptor::new(embedder_proxy))),
|
||||
cancellation_listener: Arc::new(Default::default()),
|
||||
@@ -1439,7 +1473,7 @@ fn test_fetch_with_devtools() {
|
||||
);
|
||||
|
||||
let httprequest = DevtoolsHttpRequest {
|
||||
url: url,
|
||||
url: url.url(),
|
||||
method: Method::GET,
|
||||
headers: headers,
|
||||
body: Some(vec![].into()),
|
||||
@@ -1522,7 +1556,10 @@ fn test_fetch_request_intercepted() {
|
||||
state: Arc::new(create_http_state(None)),
|
||||
user_agent: DEFAULT_USER_AGENT.into(),
|
||||
devtools_chan: None,
|
||||
filemanager: FileManager::new(embedder_proxy.clone()),
|
||||
filemanager: FileManager::new(
|
||||
embedder_proxy.clone(),
|
||||
BlobTokenCommunicator::stub_for_testing(),
|
||||
),
|
||||
file_token: FileTokenCheck::NotRequired,
|
||||
request_interceptor: Arc::new(TokioMutex::new(RequestInterceptor::new(embedder_proxy))),
|
||||
cancellation_listener: Arc::new(Default::default()),
|
||||
@@ -1538,10 +1575,14 @@ fn test_fetch_request_intercepted() {
|
||||
};
|
||||
|
||||
let url = ServoUrl::parse("http://www.example.org").unwrap();
|
||||
let request = RequestBuilder::new(Some(TEST_WEBVIEW_ID), url.clone(), Referrer::NoReferrer)
|
||||
.origin(url.origin())
|
||||
.policy_container(Default::default())
|
||||
.build();
|
||||
let request = RequestBuilder::new(
|
||||
Some(TEST_WEBVIEW_ID),
|
||||
UrlWithBlobClaim::new(url.clone(), None),
|
||||
Referrer::NoReferrer,
|
||||
)
|
||||
.origin(url.origin())
|
||||
.policy_container(Default::default())
|
||||
.build();
|
||||
let response = fetch_with_context(request, &mut context);
|
||||
|
||||
assert!(
|
||||
|
||||
@@ -13,7 +13,7 @@ use ipc_channel::ipc;
|
||||
use net::async_runtime::init_async_runtime;
|
||||
use net::embedder::NetToEmbedderMsg;
|
||||
use net::filemanager_thread::FileManager;
|
||||
use net_traits::blob_url_store::BlobURLStoreError;
|
||||
use net_traits::blob_url_store::{BlobTokenCommunicator, BlobURLStoreError};
|
||||
use net_traits::filemanager_thread::{
|
||||
FileManagerThreadError, FileManagerThreadMsg, ReadFileProgress,
|
||||
};
|
||||
@@ -32,7 +32,7 @@ fn test_filemanager() {
|
||||
servo_config::prefs::set(preferences);
|
||||
|
||||
let (embedder_proxy, embedder_receiver) = create_generic_embedder_proxy_and_receiver();
|
||||
let filemanager = FileManager::new(embedder_proxy);
|
||||
let filemanager = FileManager::new(embedder_proxy, BlobTokenCommunicator::stub_for_testing());
|
||||
|
||||
// Try to open a dummy file "components/net/tests/test.jpeg" in tree
|
||||
let mut handler = File::open("tests/test.jpeg").expect("test.jpeg is stolen");
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
use http::header::{CONTENT_LENGTH, CONTENT_RANGE, EXPIRES, HeaderValue, RANGE};
|
||||
use http::{HeaderMap, StatusCode};
|
||||
use net::http_cache::{CacheKey, HttpCache, refresh};
|
||||
use net_traits::blob_url_store::UrlWithBlobClaim;
|
||||
use net_traits::request::{Referrer, RequestBuilder};
|
||||
use net_traits::response::{Response, ResponseBody};
|
||||
use net_traits::{ResourceFetchTiming, ResourceTimingType};
|
||||
@@ -20,10 +21,14 @@ async fn test_refreshing_resource_sets_done_chan_the_appropriate_value() {
|
||||
ResponseBody::Done(vec![]),
|
||||
];
|
||||
let url = ServoUrl::parse("https://servo.org").unwrap();
|
||||
let request = RequestBuilder::new(None, url.clone(), Referrer::NoReferrer)
|
||||
.pipeline_id(Some(TEST_PIPELINE_ID))
|
||||
.origin(url.origin())
|
||||
.build();
|
||||
let request = RequestBuilder::new(
|
||||
None,
|
||||
UrlWithBlobClaim::new(url.clone(), None),
|
||||
Referrer::NoReferrer,
|
||||
)
|
||||
.pipeline_id(Some(TEST_PIPELINE_ID))
|
||||
.origin(url.origin())
|
||||
.build();
|
||||
let timing = ResourceFetchTiming::new(ResourceTimingType::Navigation);
|
||||
let mut response = Response::new(url.clone(), timing);
|
||||
// Expires header makes the response cacheable.
|
||||
@@ -71,11 +76,15 @@ async fn test_skip_incomplete_cache_for_range_request_with_no_end_bound() {
|
||||
RANGE,
|
||||
HeaderValue::from_str(&format!("bytes={}-", 0)).unwrap(),
|
||||
);
|
||||
let request = RequestBuilder::new(None, url.clone(), Referrer::NoReferrer)
|
||||
.pipeline_id(Some(TEST_PIPELINE_ID))
|
||||
.origin(url.origin())
|
||||
.headers(headers)
|
||||
.build();
|
||||
let request = RequestBuilder::new(
|
||||
None,
|
||||
UrlWithBlobClaim::new(url.clone(), None),
|
||||
Referrer::NoReferrer,
|
||||
)
|
||||
.pipeline_id(Some(TEST_PIPELINE_ID))
|
||||
.origin(url.origin())
|
||||
.headers(headers)
|
||||
.build();
|
||||
|
||||
// Store incomplete response to http_cache
|
||||
let timing = ResourceFetchTiming::new(ResourceTimingType::Navigation);
|
||||
|
||||
@@ -34,6 +34,7 @@ use net::fetch::methods::{self};
|
||||
use net::http_loader::{determine_requests_referrer, serialize_origin};
|
||||
use net::resource_thread::AuthCacheEntry;
|
||||
use net::test::DECODER_BUFFER_SIZE;
|
||||
use net_traits::blob_url_store::UrlWithBlobClaim;
|
||||
use net_traits::http_status::HttpStatus;
|
||||
use net_traits::request::{
|
||||
CredentialsMode, Destination, Referrer, Request, RequestBuilder, RequestMode,
|
||||
@@ -387,7 +388,7 @@ fn test_request_and_response_data_with_network_messages() {
|
||||
);
|
||||
|
||||
let httprequest = DevtoolsHttpRequest {
|
||||
url: url,
|
||||
url: url.url(),
|
||||
method: Method::GET,
|
||||
headers: headers,
|
||||
body: Some(vec![].into()),
|
||||
@@ -501,7 +502,7 @@ fn test_redirected_request_to_devtools() {
|
||||
let first_response = expect_response(&mut events);
|
||||
|
||||
assert_eq!(first_request.method, Method::POST);
|
||||
assert_eq!(first_request.url, pre_url);
|
||||
assert_eq!(first_request.url, pre_url.url());
|
||||
assert_eq!(
|
||||
first_response.status,
|
||||
HttpStatus::from(StatusCode::MOVED_PERMANENTLY)
|
||||
@@ -514,7 +515,7 @@ fn test_redirected_request_to_devtools() {
|
||||
let second_response = expect_response(&mut events);
|
||||
|
||||
assert_eq!(second_request.method, Method::GET);
|
||||
assert_eq!(second_request.url, post_url);
|
||||
assert_eq!(second_request.url, post_url.url());
|
||||
assert_eq!(second_response.status, HttpStatus::default());
|
||||
assert_eq!(second_request.method, second_request_update.method);
|
||||
assert_eq!(second_request.url, second_request_update.url);
|
||||
@@ -1194,7 +1195,7 @@ fn test_load_errors_when_there_a_redirect_loop() {
|
||||
};
|
||||
let (server_b, url_b) = make_server(handler_b);
|
||||
|
||||
*url_b_for_a.lock() = Some(url_b.clone());
|
||||
*url_b_for_a.lock() = Some(url_b.url());
|
||||
|
||||
let request = RequestBuilder::new(None, url_a.clone(), Referrer::NoReferrer)
|
||||
.method(Method::GET)
|
||||
@@ -1248,7 +1249,7 @@ fn test_load_succeeds_with_a_redirect_loop() {
|
||||
};
|
||||
let (server_b, url_b) = make_server(handler_b);
|
||||
|
||||
*url_b_for_a.lock() = Some(url_b.clone());
|
||||
*url_b_for_a.lock() = Some(url_b.url());
|
||||
|
||||
let request = RequestBuilder::new(None, url_a.clone(), Referrer::NoReferrer)
|
||||
.method(Method::GET)
|
||||
@@ -1264,7 +1265,7 @@ fn test_load_succeeds_with_a_redirect_loop() {
|
||||
let _ = server_b.close();
|
||||
|
||||
let response = response.to_actual();
|
||||
assert_eq!(response.url_list, [url_a.clone(), url_b, url_a]);
|
||||
assert_eq!(response.url_list, [url_a.url(), url_b.url(), url_a.url()]);
|
||||
assert_eq!(
|
||||
*response.body.lock(),
|
||||
ResponseBody::Done(b"Success".to_vec())
|
||||
@@ -1380,14 +1381,18 @@ fn test_redirect_from_x_to_y_provides_y_cookies_from_y() {
|
||||
cookie_jar.push(cookie_y, &url_y, CookieSource::HTTP);
|
||||
}
|
||||
|
||||
let request = RequestBuilder::new(None, url_x.clone(), Referrer::NoReferrer)
|
||||
.method(Method::GET)
|
||||
.destination(Destination::Document)
|
||||
.origin(mock_origin())
|
||||
.pipeline_id(Some(TEST_PIPELINE_ID))
|
||||
.credentials_mode(CredentialsMode::Include)
|
||||
.policy_container(Default::default())
|
||||
.build();
|
||||
let request = RequestBuilder::new(
|
||||
None,
|
||||
UrlWithBlobClaim::new(url_x.clone(), None),
|
||||
Referrer::NoReferrer,
|
||||
)
|
||||
.method(Method::GET)
|
||||
.destination(Destination::Document)
|
||||
.origin(mock_origin())
|
||||
.pipeline_id(Some(TEST_PIPELINE_ID))
|
||||
.credentials_mode(CredentialsMode::Include)
|
||||
.policy_container(Default::default())
|
||||
.build();
|
||||
|
||||
let response = fetch_with_context(request, &mut context);
|
||||
|
||||
@@ -1432,14 +1437,18 @@ fn test_redirect_from_x_to_x_provides_x_with_cookie_from_first_response() {
|
||||
|
||||
let url = url.join("/initial/").unwrap();
|
||||
|
||||
let request = RequestBuilder::new(None, url.clone(), Referrer::NoReferrer)
|
||||
.method(Method::GET)
|
||||
.destination(Destination::Document)
|
||||
.origin(mock_origin())
|
||||
.pipeline_id(Some(TEST_PIPELINE_ID))
|
||||
.credentials_mode(CredentialsMode::Include)
|
||||
.policy_container(Default::default())
|
||||
.build();
|
||||
let request = RequestBuilder::new(
|
||||
None,
|
||||
UrlWithBlobClaim::new(url.clone(), None),
|
||||
Referrer::NoReferrer,
|
||||
)
|
||||
.method(Method::GET)
|
||||
.destination(Destination::Document)
|
||||
.origin(mock_origin())
|
||||
.pipeline_id(Some(TEST_PIPELINE_ID))
|
||||
.credentials_mode(CredentialsMode::Include)
|
||||
.policy_container(Default::default())
|
||||
.build();
|
||||
|
||||
let response = fetch(request, None);
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ use net::test::HttpState;
|
||||
use net::test_util::{
|
||||
create_generic_embedder_proxy, make_body, make_server, make_ssl_server, replace_host_table,
|
||||
};
|
||||
use net_traits::blob_url_store::BlobTokenCommunicator;
|
||||
use net_traits::filemanager_thread::FileTokenCheck;
|
||||
use net_traits::request::Request;
|
||||
use net_traits::response::Response;
|
||||
@@ -135,7 +136,7 @@ fn new_fetch_context(
|
||||
state: Arc::new(create_http_state(Some(sender.clone()))),
|
||||
user_agent: DEFAULT_USER_AGENT.into(),
|
||||
devtools_chan,
|
||||
filemanager: FileManager::new(sender.clone()),
|
||||
filemanager: FileManager::new(sender.clone(), BlobTokenCommunicator::stub_for_testing()),
|
||||
file_token: FileTokenCheck::NotRequired,
|
||||
request_interceptor: Arc::new(TokioMutex::new(RequestInterceptor::new(sender))),
|
||||
cancellation_listener: Arc::new(Default::default()),
|
||||
|
||||
@@ -639,7 +639,7 @@ impl HTMLLinkElement {
|
||||
// Step 5. If request is null, then return.
|
||||
return;
|
||||
};
|
||||
let url = request.url.clone();
|
||||
let url = request.url.url();
|
||||
|
||||
// Step 6. Set request's initiator to "prefetch".
|
||||
let request = request.initiator(Initiator::Prefetch);
|
||||
|
||||
@@ -10,6 +10,7 @@ use euclid::default::Size2D;
|
||||
use html5ever::{LocalName, Prefix, local_name, ns};
|
||||
use js::rust::HandleObject;
|
||||
use layout_api::{HTMLMediaData, MediaMetadata};
|
||||
use net_traits::blob_url_store::UrlWithBlobClaim;
|
||||
use net_traits::image_cache::{
|
||||
ImageCache, ImageCacheResult, ImageLoadListener, ImageOrMetadataAvailable, ImageResponse,
|
||||
PendingImageId,
|
||||
@@ -232,7 +233,7 @@ impl HTMLVideoElement {
|
||||
let global = self.owner_global();
|
||||
let request = RequestBuilder::new(
|
||||
Some(document.webview_id()),
|
||||
poster_url.clone(),
|
||||
UrlWithBlobClaim::from_url_without_having_claimed_blob(poster_url.clone()),
|
||||
global.get_referrer(),
|
||||
)
|
||||
.destination(Destination::Image)
|
||||
|
||||
@@ -13,6 +13,7 @@ use embedder_traits::{EmbedderMsg, ProtocolHandlerUpdateRegistration, RegisterOr
|
||||
use headers::HeaderMap;
|
||||
use http::header::{self, HeaderValue};
|
||||
use js::rust::MutableHandleValue;
|
||||
use net_traits::blob_url_store::UrlWithBlobClaim;
|
||||
use net_traits::request::{
|
||||
CredentialsMode, Destination, RequestBuilder, RequestId, RequestMode,
|
||||
is_cors_safelisted_request_content_type,
|
||||
@@ -532,15 +533,19 @@ impl NavigatorMethods<crate::DomTypeHolder> for Navigator {
|
||||
request_body = Some(extracted_body.into_net_request_body().0);
|
||||
}
|
||||
// Step 7.1. Let req be a new request, initialized as follows:
|
||||
let request = RequestBuilder::new(None, url.clone(), global.get_referrer())
|
||||
.mode(cors_mode)
|
||||
.destination(Destination::None)
|
||||
.with_global_scope(&global)
|
||||
.method(http::Method::POST)
|
||||
.body(request_body)
|
||||
.keep_alive(true)
|
||||
.credentials_mode(CredentialsMode::Include)
|
||||
.headers(headers);
|
||||
let request = RequestBuilder::new(
|
||||
None,
|
||||
UrlWithBlobClaim::from_url_without_having_claimed_blob(url.clone()),
|
||||
global.get_referrer(),
|
||||
)
|
||||
.mode(cors_mode)
|
||||
.destination(Destination::None)
|
||||
.with_global_scope(&global)
|
||||
.method(http::Method::POST)
|
||||
.body(request_body)
|
||||
.keep_alive(true)
|
||||
.credentials_mode(CredentialsMode::Include)
|
||||
.headers(headers);
|
||||
// Step 7.2. Fetch req.
|
||||
global.fetch(
|
||||
request,
|
||||
|
||||
@@ -894,7 +894,7 @@ impl Notification {
|
||||
let request_id = request.id;
|
||||
|
||||
let cache_result = global.image_cache().get_cached_image_status(
|
||||
request.url.clone(),
|
||||
request.url.url(),
|
||||
global.origin().immutable().clone(),
|
||||
None, // TODO: check which CORS should be used
|
||||
);
|
||||
@@ -1021,7 +1021,7 @@ impl Notification {
|
||||
pending_image_id,
|
||||
image_cache: global.image_cache(),
|
||||
notification: Trusted::new(self),
|
||||
url: request.url.clone(),
|
||||
url: request.url.url(),
|
||||
status: Ok(()),
|
||||
};
|
||||
|
||||
|
||||
@@ -306,7 +306,7 @@ impl LinkProcessingOptions {
|
||||
// Step 9. Let controller be null.
|
||||
// Step 10. Let reportTiming given a Document document be to report timing for controller
|
||||
// given document's relevant global object.
|
||||
let url = request.url.clone();
|
||||
let url = request.url.url();
|
||||
let fetch_context = LinkFetchContext {
|
||||
url,
|
||||
link,
|
||||
|
||||
@@ -12,6 +12,7 @@ use http::header::{HeaderName, HeaderValue};
|
||||
use http::method::InvalidMethod;
|
||||
use js::rust::HandleObject;
|
||||
use net_traits::ReferrerPolicy as MsgReferrerPolicy;
|
||||
use net_traits::blob_url_store::UrlWithBlobClaim;
|
||||
use net_traits::fetch::headers::is_forbidden_method;
|
||||
use net_traits::request::{
|
||||
CacheMode as NetTraitsRequestCache, CredentialsMode as NetTraitsRequestCredentials,
|
||||
@@ -573,9 +574,13 @@ impl Request {
|
||||
}
|
||||
|
||||
fn net_request_from_global(global: &GlobalScope, url: ServoUrl) -> NetTraitsRequest {
|
||||
RequestBuilder::new(global.webview_id(), url, global.get_referrer())
|
||||
.with_global_scope(global)
|
||||
.build()
|
||||
RequestBuilder::new(
|
||||
global.webview_id(),
|
||||
UrlWithBlobClaim::from_url_without_having_claimed_blob(url),
|
||||
global.get_referrer(),
|
||||
)
|
||||
.with_global_scope(global)
|
||||
.build()
|
||||
}
|
||||
|
||||
/// <https://fetch.spec.whatwg.org/#concept-method-normalize>
|
||||
|
||||
@@ -13,6 +13,7 @@ use js::jsval::UndefinedValue;
|
||||
use js::realm::AutoRealm;
|
||||
use js::rust::{CustomAutoRooterGuard, HandleObject};
|
||||
use js::typedarray::{ArrayBuffer, ArrayBufferView, CreateWith};
|
||||
use net_traits::blob_url_store::UrlWithBlobClaim;
|
||||
use net_traits::request::{
|
||||
CacheMode, CredentialsMode, RedirectMode, Referrer, RequestBuilder, RequestMode,
|
||||
ServiceWorkersMode,
|
||||
@@ -278,7 +279,7 @@ impl WebSocketMethods<crate::DomTypeHolder> for WebSocket {
|
||||
// "include", cache mode is "no-store" , and redirect mode is "error"
|
||||
let request = RequestBuilder::new(
|
||||
global.webview_id(),
|
||||
url_record.clone(),
|
||||
UrlWithBlobClaim::from_url_without_having_claimed_blob(url_record.clone()),
|
||||
Referrer::NoReferrer,
|
||||
)
|
||||
.with_global_scope(global)
|
||||
|
||||
@@ -14,6 +14,7 @@ use js::context::JSContext;
|
||||
use js::jsapi::{Heap, JSContext as RawJSContext, JSObject};
|
||||
use js::jsval::UndefinedValue;
|
||||
use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
|
||||
use net_traits::blob_url_store::UrlWithBlobClaim;
|
||||
use net_traits::image_cache::ImageCache;
|
||||
use net_traits::policy_container::{PolicyContainer, RequestPolicyContainer};
|
||||
use net_traits::request::{
|
||||
@@ -359,7 +360,7 @@ impl DedicatedWorkerGlobalScope {
|
||||
pub(crate) fn run_worker_scope(
|
||||
mut init: WorkerGlobalScopeInit,
|
||||
webview_id: WebViewId,
|
||||
worker_url: ServoUrl,
|
||||
worker_url: UrlWithBlobClaim,
|
||||
from_devtools_receiver: GenericReceiver<DevtoolScriptControlMsg>,
|
||||
worker: TrustedWorkerAddress,
|
||||
parent_event_loop_sender: ScriptEventLoopSender,
|
||||
@@ -474,7 +475,7 @@ impl DedicatedWorkerGlobalScope {
|
||||
webview_id,
|
||||
worker_name.into(),
|
||||
worker_type,
|
||||
worker_url.clone(),
|
||||
worker_url.url(),
|
||||
devtools_mpsc_port,
|
||||
runtime,
|
||||
parent_event_loop_sender,
|
||||
@@ -526,7 +527,7 @@ impl DedicatedWorkerGlobalScope {
|
||||
WorkerType::Module => {
|
||||
fetch_a_module_worker_script_graph(
|
||||
cx,
|
||||
worker_url,
|
||||
worker_url.url(),
|
||||
fetch_client,
|
||||
ModuleOwner::Worker(Trusted::new(scope)),
|
||||
referrer,
|
||||
@@ -804,14 +805,14 @@ pub(crate) unsafe extern "C" fn interrupt_callback(cx: *mut RawJSContext) -> boo
|
||||
/// <https://html.spec.whatwg.org/multipage/#fetch-a-classic-worker-script>
|
||||
fn fetch_a_classic_worker_script(
|
||||
workerscope: &WorkerGlobalScope,
|
||||
url: ServoUrl,
|
||||
url_with_blob_lock: UrlWithBlobClaim,
|
||||
fetch_client: ModuleFetchClient,
|
||||
destination: Destination,
|
||||
webview_id: WebViewId,
|
||||
referrer: Referrer,
|
||||
) {
|
||||
// Step 1. Let request be a new request whose URL is url,
|
||||
let request = RequestBuilder::new(Some(webview_id), url.clone(), referrer)
|
||||
let request = RequestBuilder::new(Some(webview_id), url_with_blob_lock.clone(), referrer)
|
||||
// client is fetchClient,
|
||||
.insecure_requests_policy(fetch_client.insecure_requests_policy)
|
||||
.has_trustworthy_ancestor_origin(fetch_client.has_trustworthy_ancestor_origin)
|
||||
@@ -834,7 +835,7 @@ fn fetch_a_classic_worker_script(
|
||||
|
||||
let context = ScriptFetchContext::new(
|
||||
Trusted::new(workerscope),
|
||||
url,
|
||||
url_with_blob_lock.url(),
|
||||
fetch_client.policy_container,
|
||||
);
|
||||
let global = workerscope.upcast::<GlobalScope>();
|
||||
|
||||
@@ -14,6 +14,7 @@ use fonts::FontContext;
|
||||
use js::jsapi::{JS_AddInterruptCallback, JSContext};
|
||||
use js::jsval::UndefinedValue;
|
||||
use net_traits::CustomResponseMediator;
|
||||
use net_traits::blob_url_store::UrlWithBlobClaim;
|
||||
use net_traits::request::{
|
||||
CredentialsMode, Destination, InsecureRequestsPolicy, ParserMetadata, Referrer, RequestBuilder,
|
||||
};
|
||||
@@ -409,17 +410,21 @@ impl ServiceWorkerGlobalScope {
|
||||
.map(Referrer::ReferrerUrl)
|
||||
.unwrap_or_else(|| global_scope.get_referrer());
|
||||
|
||||
let request = RequestBuilder::new(None, script_url, referrer)
|
||||
.destination(Destination::ServiceWorker)
|
||||
.credentials_mode(CredentialsMode::Include)
|
||||
.parser_metadata(ParserMetadata::NotParserInserted)
|
||||
.use_url_credentials(true)
|
||||
.pipeline_id(Some(pipeline_id))
|
||||
.referrer_policy(referrer_policy)
|
||||
.insecure_requests_policy(worker_scope.insecure_requests_policy())
|
||||
// TODO: Use policy container from ScopeThings
|
||||
.policy_container(global_scope.policy_container())
|
||||
.origin(origin);
|
||||
let request = RequestBuilder::new(
|
||||
None,
|
||||
UrlWithBlobClaim::from_url_without_having_claimed_blob(script_url),
|
||||
referrer,
|
||||
)
|
||||
.destination(Destination::ServiceWorker)
|
||||
.credentials_mode(CredentialsMode::Include)
|
||||
.parser_metadata(ParserMetadata::NotParserInserted)
|
||||
.use_url_credentials(true)
|
||||
.pipeline_id(Some(pipeline_id))
|
||||
.referrer_policy(referrer_policy)
|
||||
.insecure_requests_policy(worker_scope.insecure_requests_policy())
|
||||
// TODO: Use policy container from ScopeThings
|
||||
.policy_container(global_scope.policy_container())
|
||||
.origin(origin);
|
||||
|
||||
let (url, source) = match load_whole_resource(
|
||||
request,
|
||||
|
||||
@@ -42,6 +42,7 @@ use crate::dom::workerglobalscope::prepare_workerscope_init;
|
||||
use crate::realms::enter_auto_realm;
|
||||
use crate::script_runtime::{CanGc, ThreadSafeJSContext};
|
||||
use crate::task::TaskOnce;
|
||||
use crate::url::ensure_blob_referenced_by_url_is_kept_alive;
|
||||
|
||||
pub(crate) type TrustedWorkerAddress = Trusted<Worker>;
|
||||
|
||||
@@ -161,9 +162,9 @@ impl Worker {
|
||||
}
|
||||
|
||||
impl WorkerMethods<crate::DomTypeHolder> for Worker {
|
||||
// https://html.spec.whatwg.org/multipage/#dom-worker
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-worker>
|
||||
fn Constructor(
|
||||
cx: &mut js::context::JSContext,
|
||||
cx: &mut JSContext,
|
||||
global: &GlobalScope,
|
||||
proto: Option<HandleObject>,
|
||||
script_url: TrustedScriptURLOrUSVString,
|
||||
@@ -178,10 +179,17 @@ impl WorkerMethods<crate::DomTypeHolder> for Worker {
|
||||
script_url,
|
||||
"Worker constructor",
|
||||
)?;
|
||||
// Step 2-4.
|
||||
let worker_url = match global.encoding_parse_a_url(&compliant_script_url.str()) {
|
||||
Ok(url) => url,
|
||||
Err(_) => return Err(Error::Syntax(None)),
|
||||
// Step 2. Let outsideSettings be this's relevant settings object.
|
||||
// Step 3. Let workerURL be the result of encoding-parsing a URL given compliantScriptURL,
|
||||
// relative to outsideSettings.
|
||||
// TODO: Locking the URL should eventually happen inside encoding_parse_a_url, since most callers
|
||||
// will expect their blobs to be kept alive...
|
||||
let Ok(worker_url) = global
|
||||
.encoding_parse_a_url(&compliant_script_url.str())
|
||||
.map(|url| ensure_blob_referenced_by_url_is_kept_alive(global, url))
|
||||
else {
|
||||
// Step 4. If workerURL is failure, then throw a "SyntaxError" DOMException.
|
||||
return Err(Error::Syntax(None));
|
||||
};
|
||||
|
||||
let (sender, receiver) = unbounded();
|
||||
@@ -221,11 +229,11 @@ impl WorkerMethods<crate::DomTypeHolder> for Worker {
|
||||
let worker_id = WorkerId(Uuid::new_v4());
|
||||
if let Some(chan) = global.devtools_chan() {
|
||||
let pipeline_id = global.pipeline_id();
|
||||
let title = format!("Worker for {}", worker_url);
|
||||
let title = format!("Worker for {}", worker_url.url());
|
||||
if let Some(browsing_context) = browsing_context {
|
||||
let page_info = DevtoolsPageInfo {
|
||||
title,
|
||||
url: worker_url.clone(),
|
||||
url: worker_url.url(),
|
||||
is_top_level_global: false,
|
||||
is_service_worker: false,
|
||||
};
|
||||
|
||||
@@ -18,6 +18,7 @@ use headers::{HeaderMapExt, ReferrerPolicy as ReferrerPolicyHeader};
|
||||
use js::realm::CurrentRealm;
|
||||
use js::rust::{HandleValue, MutableHandleValue, ParentRuntime};
|
||||
use mime::Mime;
|
||||
use net_traits::blob_url_store::UrlWithBlobClaim;
|
||||
use net_traits::policy_container::PolicyContainer;
|
||||
use net_traits::request::{
|
||||
CredentialsMode, Destination, InsecureRequestsPolicy, ParserMetadata, RequestBuilder, RequestId,
|
||||
@@ -696,7 +697,7 @@ impl WorkerGlobalScopeMethods<crate::DomTypeHolder> for WorkerGlobalScope {
|
||||
let global_scope = self.upcast::<GlobalScope>();
|
||||
let request = RequestBuilder::new(
|
||||
global_scope.webview_id(),
|
||||
url.clone(),
|
||||
UrlWithBlobClaim::from_url_without_having_claimed_blob(url.clone()),
|
||||
global_scope.get_referrer(),
|
||||
)
|
||||
.destination(Destination::Script)
|
||||
|
||||
@@ -23,6 +23,7 @@ use dom_struct::dom_struct;
|
||||
use js::jsapi::{GCReason, JSGCParamKey, JSTracer};
|
||||
use js::rust::wrappers2::{JS_GC, JS_GetGCParameter};
|
||||
use malloc_size_of::malloc_size_of_is_0;
|
||||
use net_traits::blob_url_store::UrlWithBlobClaim;
|
||||
use net_traits::policy_container::PolicyContainer;
|
||||
use net_traits::request::{Destination, RequestBuilder, RequestMode};
|
||||
use rustc_hash::FxHashMap;
|
||||
@@ -688,12 +689,16 @@ impl WorkletThread {
|
||||
// TODO: Caching.
|
||||
let global = global_scope.upcast::<GlobalScope>();
|
||||
let resource_fetcher = self.global_init.resource_threads.sender();
|
||||
let request = RequestBuilder::new(None, script_url, global.get_referrer())
|
||||
.destination(Destination::Script)
|
||||
.mode(RequestMode::CorsMode)
|
||||
.credentials_mode(credentials.convert())
|
||||
.policy_container(policy_container)
|
||||
.origin(origin);
|
||||
let request = RequestBuilder::new(
|
||||
None,
|
||||
UrlWithBlobClaim::from_url_without_having_claimed_blob(script_url),
|
||||
global.get_referrer(),
|
||||
)
|
||||
.destination(Destination::Script)
|
||||
.mode(RequestMode::CorsMode)
|
||||
.credentials_mode(credentials.convert())
|
||||
.policy_container(policy_container)
|
||||
.origin(origin);
|
||||
|
||||
let script = load_whole_resource(
|
||||
request,
|
||||
|
||||
@@ -25,6 +25,7 @@ use js::jsval::{JSVal, NullValue};
|
||||
use js::rust::wrappers::JS_ParseJSON;
|
||||
use js::rust::{HandleObject, MutableHandleValue};
|
||||
use js::typedarray::{ArrayBufferU8, HeapArrayBuffer};
|
||||
use net_traits::blob_url_store::UrlWithBlobClaim;
|
||||
use net_traits::fetch::headers::extract_mime_type_as_dataurl_mime;
|
||||
use net_traits::http_status::HttpStatus;
|
||||
use net_traits::request::{CredentialsMode, Referrer, RequestBuilder, RequestId, RequestMode};
|
||||
@@ -679,7 +680,9 @@ impl XMLHttpRequestMethods<crate::DomTypeHolder> for XMLHttpRequest {
|
||||
let global = self.global();
|
||||
let mut request = RequestBuilder::new(
|
||||
global.webview_id(),
|
||||
self.request_url.borrow().clone().unwrap(),
|
||||
UrlWithBlobClaim::from_url_without_having_claimed_blob(
|
||||
self.request_url.borrow().clone().unwrap(),
|
||||
),
|
||||
self.referrer.clone(),
|
||||
)
|
||||
.method(self.request_method.borrow().clone())
|
||||
@@ -1585,7 +1588,7 @@ impl XMLHttpRequest {
|
||||
xhr,
|
||||
gen_id: self.generation_id.get(),
|
||||
sync_status: sync_status.clone(),
|
||||
url: request_builder.url.clone(),
|
||||
url: request_builder.url.url(),
|
||||
};
|
||||
|
||||
let (task_source, script_port) = if self.sync.get() {
|
||||
|
||||
@@ -12,6 +12,7 @@ use js::jsval::UndefinedValue;
|
||||
use js::realm::CurrentRealm;
|
||||
use js::rust::HandleValue;
|
||||
use js::rust::wrappers::JS_SetPendingException;
|
||||
use net_traits::blob_url_store::UrlWithBlobClaim;
|
||||
use net_traits::request::{
|
||||
CorsSettings, CredentialsMode, Destination, Referrer, Request as NetTraitsRequest,
|
||||
RequestBuilder, RequestId, RequestMode, ServiceWorkersMode,
|
||||
@@ -143,31 +144,34 @@ pub(crate) struct FetchGroup {
|
||||
}
|
||||
|
||||
fn request_init_from_request(request: NetTraitsRequest, global: &GlobalScope) -> RequestBuilder {
|
||||
let mut builder =
|
||||
RequestBuilder::new(request.target_webview_id, request.url(), request.referrer)
|
||||
.method(request.method)
|
||||
.headers(request.headers)
|
||||
.unsafe_request(request.unsafe_request)
|
||||
.body(request.body)
|
||||
.destination(request.destination)
|
||||
.synchronous(request.synchronous)
|
||||
.mode(request.mode)
|
||||
.cache_mode(request.cache_mode)
|
||||
.use_cors_preflight(request.use_cors_preflight)
|
||||
.credentials_mode(request.credentials_mode)
|
||||
.use_url_credentials(request.use_url_credentials)
|
||||
.referrer_policy(request.referrer_policy)
|
||||
.pipeline_id(request.pipeline_id)
|
||||
.redirect_mode(request.redirect_mode)
|
||||
.integrity_metadata(request.integrity_metadata)
|
||||
.cryptographic_nonce_metadata(request.cryptographic_nonce_metadata)
|
||||
.parser_metadata(request.parser_metadata)
|
||||
.initiator(request.initiator)
|
||||
.client(global.request_client())
|
||||
.insecure_requests_policy(request.insecure_requests_policy)
|
||||
.has_trustworthy_ancestor_origin(request.has_trustworthy_ancestor_origin)
|
||||
.https_state(request.https_state)
|
||||
.response_tainting(request.response_tainting);
|
||||
let mut builder = RequestBuilder::new(
|
||||
request.target_webview_id,
|
||||
UrlWithBlobClaim::from_url_without_having_claimed_blob(request.url()),
|
||||
request.referrer,
|
||||
)
|
||||
.method(request.method)
|
||||
.headers(request.headers)
|
||||
.unsafe_request(request.unsafe_request)
|
||||
.body(request.body)
|
||||
.destination(request.destination)
|
||||
.synchronous(request.synchronous)
|
||||
.mode(request.mode)
|
||||
.cache_mode(request.cache_mode)
|
||||
.use_cors_preflight(request.use_cors_preflight)
|
||||
.credentials_mode(request.credentials_mode)
|
||||
.use_url_credentials(request.use_url_credentials)
|
||||
.referrer_policy(request.referrer_policy)
|
||||
.pipeline_id(request.pipeline_id)
|
||||
.redirect_mode(request.redirect_mode)
|
||||
.integrity_metadata(request.integrity_metadata)
|
||||
.cryptographic_nonce_metadata(request.cryptographic_nonce_metadata)
|
||||
.parser_metadata(request.parser_metadata)
|
||||
.initiator(request.initiator)
|
||||
.client(global.request_client())
|
||||
.insecure_requests_policy(request.insecure_requests_policy)
|
||||
.has_trustworthy_ancestor_origin(request.has_trustworthy_ancestor_origin)
|
||||
.https_state(request.https_state)
|
||||
.response_tainting(request.response_tainting);
|
||||
builder.id = request.id;
|
||||
builder
|
||||
}
|
||||
@@ -276,7 +280,7 @@ pub(crate) fn Fetch(
|
||||
global: Trusted::new(global),
|
||||
locally_aborted: false,
|
||||
canceller: FetchCanceller::new(request_id, keep_alive, global.core_resource_thread()),
|
||||
url: request_init.url.clone(),
|
||||
url: request_init.url.url(),
|
||||
};
|
||||
let network_listener = NetworkListener::new(
|
||||
fetch_context,
|
||||
@@ -741,7 +745,7 @@ pub(crate) fn load_whole_resource(
|
||||
) -> Result<(Metadata, Vec<u8>, bool), NetworkError> {
|
||||
let request = request.https_state(global.get_https_state());
|
||||
let (action_sender, action_receiver) = ipc::channel().unwrap();
|
||||
let url = request.url.clone();
|
||||
let url = request.url.url();
|
||||
core_resource_thread
|
||||
.send(CoreResourceMsg::Fetch(
|
||||
request,
|
||||
@@ -805,22 +809,26 @@ pub(crate) fn create_a_potential_cors_request(
|
||||
same_origin_fallback: Option<bool>,
|
||||
referrer: Referrer,
|
||||
) -> RequestBuilder {
|
||||
RequestBuilder::new(webview_id, url, referrer)
|
||||
// Step 1. Let mode be "no-cors" if corsAttributeState is No CORS, and "cors" otherwise.
|
||||
.mode(match cors_setting {
|
||||
Some(_) => RequestMode::CorsMode,
|
||||
// Step 2. If same-origin fallback flag is set and mode is "no-cors", set mode to "same-origin".
|
||||
None if same_origin_fallback == Some(true) => RequestMode::SameOrigin,
|
||||
None => RequestMode::NoCors,
|
||||
})
|
||||
.credentials_mode(match cors_setting {
|
||||
// Step 4. If corsAttributeState is Anonymous, set credentialsMode to "same-origin".
|
||||
Some(CorsSettings::Anonymous) => CredentialsMode::CredentialsSameOrigin,
|
||||
// Step 3. Let credentialsMode be "include".
|
||||
_ => CredentialsMode::Include,
|
||||
})
|
||||
// Step 5. Return a new request whose URL is url, destination is destination,
|
||||
// mode is mode, credentials mode is credentialsMode, and whose use-URL-credentials flag is set.
|
||||
.destination(destination)
|
||||
.use_url_credentials(true)
|
||||
RequestBuilder::new(
|
||||
webview_id,
|
||||
UrlWithBlobClaim::from_url_without_having_claimed_blob(url),
|
||||
referrer,
|
||||
)
|
||||
// Step 1. Let mode be "no-cors" if corsAttributeState is No CORS, and "cors" otherwise.
|
||||
.mode(match cors_setting {
|
||||
Some(_) => RequestMode::CorsMode,
|
||||
// Step 2. If same-origin fallback flag is set and mode is "no-cors", set mode to "same-origin".
|
||||
None if same_origin_fallback == Some(true) => RequestMode::SameOrigin,
|
||||
None => RequestMode::NoCors,
|
||||
})
|
||||
.credentials_mode(match cors_setting {
|
||||
// Step 4. If corsAttributeState is Anonymous, set credentialsMode to "same-origin".
|
||||
Some(CorsSettings::Anonymous) => CredentialsMode::CredentialsSameOrigin,
|
||||
// Step 3. Let credentialsMode be "include".
|
||||
_ => CredentialsMode::Include,
|
||||
})
|
||||
// Step 5. Return a new request whose URL is url, destination is destination,
|
||||
// mode is mode, credentials mode is credentialsMode, and whose use-URL-credentials flag is set.
|
||||
.destination(destination)
|
||||
.use_url_credentials(true)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use net_traits::blob_url_store::UrlWithBlobClaim;
|
||||
use net_traits::image_cache::{ImageCache, PendingImageId};
|
||||
use net_traits::request::{Destination, RequestBuilder, RequestId};
|
||||
use net_traits::{FetchMetadata, FetchResponseMsg, NetworkError, ResourceFetchTiming};
|
||||
@@ -102,9 +103,13 @@ pub(crate) fn fetch_image_for_layout(
|
||||
};
|
||||
|
||||
let global = node.owner_global();
|
||||
let request = RequestBuilder::new(Some(document.webview_id()), url, global.get_referrer())
|
||||
.destination(Destination::Image)
|
||||
.with_global_scope(&global);
|
||||
let request = RequestBuilder::new(
|
||||
Some(document.webview_id()),
|
||||
UrlWithBlobClaim::from_url_without_having_claimed_blob(url),
|
||||
global.get_referrer(),
|
||||
)
|
||||
.destination(Destination::Image)
|
||||
.with_global_scope(&global);
|
||||
|
||||
// Layout image loads do not delay the document load event.
|
||||
document.fetch_background(request, context);
|
||||
|
||||
@@ -37,6 +37,7 @@ pub(crate) mod fetch;
|
||||
pub(crate) mod indexeddb;
|
||||
mod init;
|
||||
mod layout_image;
|
||||
mod url;
|
||||
|
||||
pub(crate) mod document_collection;
|
||||
pub(crate) mod iframe_collection;
|
||||
|
||||
@@ -14,6 +14,7 @@ use embedder_traits::user_contents::UserContentManagerId;
|
||||
use embedder_traits::{Theme, ViewportDetails, WebDriverLoadStatus};
|
||||
use http::header;
|
||||
use js::context::JSContext;
|
||||
use net_traits::blob_url_store::UrlWithBlobClaim;
|
||||
use net_traits::policy_container::RequestPolicyContainer;
|
||||
use net_traits::request::{
|
||||
CredentialsMode, InsecureRequestsPolicy, Origin, PreloadedResources, RedirectMode,
|
||||
@@ -236,7 +237,7 @@ impl InProgressLoad {
|
||||
|
||||
let mut request_builder = RequestBuilder::new(
|
||||
Some(webview_id),
|
||||
self.load_data.url.clone(),
|
||||
UrlWithBlobClaim::from_url_without_having_claimed_blob(self.load_data.url.clone()),
|
||||
self.load_data.referrer.clone(),
|
||||
)
|
||||
.method(self.load_data.method.clone())
|
||||
|
||||
@@ -41,6 +41,7 @@ use js::rust::{
|
||||
transform_str_to_source_text,
|
||||
};
|
||||
use mime::Mime;
|
||||
use net_traits::blob_url_store::UrlWithBlobClaim;
|
||||
use net_traits::http_status::HttpStatus;
|
||||
use net_traits::mime_classifier::MimeClassifier;
|
||||
use net_traits::policy_container::PolicyContainer;
|
||||
@@ -1592,21 +1593,25 @@ pub(crate) fn fetch_a_single_module_script(
|
||||
// TODO Step 11. Set request's initiator type to "script".
|
||||
|
||||
// Step 12. Set up the module script request given request and options.
|
||||
let request = RequestBuilder::new(webview_id, url.clone(), referrer)
|
||||
.destination(destination)
|
||||
.parser_metadata(options.parser_metadata)
|
||||
.integrity_metadata(options.integrity_metadata.clone())
|
||||
.credentials_mode(options.credentials_mode)
|
||||
.referrer_policy(options.referrer_policy)
|
||||
.mode(mode)
|
||||
.cryptographic_nonce_metadata(options.cryptographic_nonce.clone())
|
||||
.insecure_requests_policy(fetch_client.insecure_requests_policy)
|
||||
.has_trustworthy_ancestor_origin(fetch_client.has_trustworthy_ancestor_origin)
|
||||
.policy_container(fetch_client.policy_container)
|
||||
.client(fetch_client.client)
|
||||
.pipeline_id(Some(fetch_client.pipeline_id))
|
||||
.origin(fetch_client.origin)
|
||||
.https_state(fetch_client.https_state);
|
||||
let request = RequestBuilder::new(
|
||||
webview_id,
|
||||
UrlWithBlobClaim::from_url_without_having_claimed_blob(url.clone()),
|
||||
referrer,
|
||||
)
|
||||
.destination(destination)
|
||||
.parser_metadata(options.parser_metadata)
|
||||
.integrity_metadata(options.integrity_metadata.clone())
|
||||
.credentials_mode(options.credentials_mode)
|
||||
.referrer_policy(options.referrer_policy)
|
||||
.mode(mode)
|
||||
.cryptographic_nonce_metadata(options.cryptographic_nonce.clone())
|
||||
.insecure_requests_policy(fetch_client.insecure_requests_policy)
|
||||
.has_trustworthy_ancestor_origin(fetch_client.has_trustworthy_ancestor_origin)
|
||||
.policy_container(fetch_client.policy_container)
|
||||
.client(fetch_client.client)
|
||||
.pipeline_id(Some(fetch_client.pipeline_id))
|
||||
.origin(fetch_client.origin)
|
||||
.https_state(fetch_client.https_state);
|
||||
|
||||
let context = ModuleContext {
|
||||
owner,
|
||||
|
||||
26
components/script/url.rs
Normal file
26
components/script/url.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
/* 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 net_traits::blob_url_store::{BlobResolver, UrlWithBlobClaim};
|
||||
use servo_url::ServoUrl;
|
||||
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
|
||||
pub(crate) fn ensure_blob_referenced_by_url_is_kept_alive(
|
||||
global: &GlobalScope,
|
||||
url: ServoUrl,
|
||||
) -> UrlWithBlobClaim {
|
||||
match UrlWithBlobClaim::for_url(url) {
|
||||
Ok(lock) => lock,
|
||||
Err(url) => {
|
||||
let token = BlobResolver {
|
||||
origin: global.api_base_url().origin(),
|
||||
resource_threads: global.resource_threads(),
|
||||
}
|
||||
.acquire_blob_token_for(&url);
|
||||
|
||||
UrlWithBlobClaim::new(url, token)
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ fn test_cache_entries() {
|
||||
let port = url.port().unwrap();
|
||||
|
||||
delegate.reset();
|
||||
webview.load(url.clone().into_url());
|
||||
webview.load(url.as_url().clone());
|
||||
let delegate_clone = delegate.clone();
|
||||
servo_test.spin(move || !delegate_clone.url_changed.get());
|
||||
|
||||
@@ -80,7 +80,7 @@ fn test_clear_cache() {
|
||||
|
||||
let _webview = WebViewBuilder::new(servo_test.servo(), servo_test.rendering_context.clone())
|
||||
.delegate(delegate.clone())
|
||||
.url(url.into_url())
|
||||
.url(url.as_url().clone())
|
||||
.build();
|
||||
|
||||
servo_test.spin(move || !delegate.url_changed.get());
|
||||
|
||||
@@ -15,6 +15,7 @@ use hyper::body::{Bytes, Incoming};
|
||||
use hyper::{Request as HyperRequest, Response as HyperResponse};
|
||||
use net::test_util::{Server, make_body, make_server, replace_host_table};
|
||||
use net_traits::CookieSource;
|
||||
use net_traits::blob_url_store::UrlWithBlobClaim;
|
||||
use servo::{JSValue, Servo, ServoUrl, SiteData, StorageType, WebView, WebViewBuilder};
|
||||
|
||||
use crate::common::{ServoTest, WebViewDelegateImpl, evaluate_javascript};
|
||||
@@ -79,7 +80,7 @@ fn run_test_site_data_steps(webview_test: &WebViewTest, steps: &[TestSiteDataSte
|
||||
*response.body_mut() = make_body(MESSAGE.to_vec());
|
||||
};
|
||||
|
||||
let mut servers: Vec<(Server, ServoUrl)> =
|
||||
let mut servers: Vec<(Server, UrlWithBlobClaim)> =
|
||||
(0..steps.len()).map(|_| make_server(handler)).collect();
|
||||
servers.sort_by(|(_, a), (_, b)| a.cmp(b));
|
||||
|
||||
@@ -588,7 +589,7 @@ fn test_clear_cookies() {
|
||||
|
||||
let webview = WebViewBuilder::new(servo_test.servo(), servo_test.rendering_context.clone())
|
||||
.delegate(delegate.clone())
|
||||
.url(url.into_url())
|
||||
.url(url.as_url().clone())
|
||||
.build();
|
||||
|
||||
servo_test.spin(move || !delegate.url_changed.get());
|
||||
@@ -633,7 +634,7 @@ fn test_get_cookie() {
|
||||
let delegate = Rc::new(WebViewDelegateImpl::default());
|
||||
let _webview = WebViewBuilder::new(servo_test.servo(), servo_test.rendering_context.clone())
|
||||
.delegate(delegate.clone())
|
||||
.url(url.clone().into_url())
|
||||
.url(url.url().into_url())
|
||||
.build();
|
||||
|
||||
// Wait for LoadStatus::Complete to ensure the HTTP response and Set-Cookie header are processed.
|
||||
@@ -643,7 +644,7 @@ fn test_get_cookie() {
|
||||
let cookies = servo_test
|
||||
.servo()
|
||||
.site_data_manager()
|
||||
.cookies_for_url(url.into_url(), CookieSource::NonHTTP);
|
||||
.cookies_for_url(url.url().into_url(), CookieSource::NonHTTP);
|
||||
assert_eq!(cookies.len(), 1);
|
||||
assert_eq!(cookies[0].name(), "foo");
|
||||
assert_eq!(cookies[0].value(), "bar");
|
||||
@@ -666,7 +667,7 @@ fn test_set_cookie() {
|
||||
*response.body_mut() = make_body(b"<!DOCTYPE html><p>hi</p>".to_vec());
|
||||
};
|
||||
let (server, url) = make_server(handler);
|
||||
let page_url = url.clone().into_url();
|
||||
let page_url = url.url().into_url();
|
||||
|
||||
let delegate = Rc::new(WebViewDelegateImpl::default());
|
||||
let delegate_clone = delegate.clone();
|
||||
|
||||
@@ -47,7 +47,7 @@ fn test_user_content_manager_user_script() {
|
||||
|
||||
let webview = WebViewBuilder::new(servo_test.servo(), servo_test.rendering_context.clone())
|
||||
.user_content_manager(user_content_manager.clone())
|
||||
.url(url.into_url())
|
||||
.url(url.as_url().clone())
|
||||
.build();
|
||||
|
||||
let result = evaluate_javascript(&servo_test, webview.clone(), "window.fromUserContentScript");
|
||||
|
||||
@@ -96,7 +96,7 @@ fn test_create_webview_http() {
|
||||
|
||||
let webview = WebViewBuilder::new(servo_test.servo(), servo_test.rendering_context.clone())
|
||||
.delegate(delegate.clone())
|
||||
.url(url.into_url())
|
||||
.url(url.as_url().clone())
|
||||
.build();
|
||||
|
||||
servo_test.spin(move || !delegate.url_changed.get());
|
||||
@@ -957,7 +957,7 @@ fn test_webview_load_with_headers() {
|
||||
|
||||
let webview = WebViewBuilder::new(servo_test.servo(), servo_test.rendering_context.clone())
|
||||
.delegate(delegate.clone())
|
||||
.url(url.clone().into_url())
|
||||
.url(url.url().into_url())
|
||||
.build();
|
||||
|
||||
{
|
||||
@@ -965,7 +965,7 @@ fn test_webview_load_with_headers() {
|
||||
servo_test.spin(move || !delegate.url_changed.get());
|
||||
}
|
||||
|
||||
let urlrequest = UrlRequest::new(url.into_url()).headers(headers);
|
||||
let urlrequest = UrlRequest::new(url.url().into_url()).headers(headers);
|
||||
webview.load_request(urlrequest);
|
||||
{
|
||||
let request_count = request_count.clone();
|
||||
|
||||
@@ -2,13 +2,24 @@
|
||||
* 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::fmt;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use malloc_size_of_derive::MallocSizeOf;
|
||||
use parking_lot::Mutex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use servo_base::generic_channel::{self, GenericSend, GenericSender};
|
||||
use servo_url::{ImmutableOrigin, ServoUrl};
|
||||
use url::Url;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
BlobTokenRefreshRequest, BlobTokenRevocationRequest, CoreResourceMsg, FileManagerThreadMsg,
|
||||
ResourceThreads,
|
||||
};
|
||||
|
||||
/// Errors returned to Blob URL Store request
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
||||
pub enum BlobURLStoreError {
|
||||
@@ -39,6 +50,11 @@ pub struct BlobBuf {
|
||||
/// Parse URL as Blob URL scheme's definition
|
||||
///
|
||||
/// <https://w3c.github.io/FileAPI/#url-intro>
|
||||
///
|
||||
/// FIXME: This function should never be used to obtain the origin of a blob url, because
|
||||
/// it doesn't consider [blob URL entries].
|
||||
///
|
||||
/// [blob URL entries]: https://url.spec.whatwg.org/#concept-url-blob-entry
|
||||
pub fn parse_blob_url(url: &ServoUrl) -> Result<(Uuid, ImmutableOrigin), &'static str> {
|
||||
if url.query().is_some() {
|
||||
return Err("URL should not contain a query");
|
||||
@@ -60,3 +76,261 @@ pub fn parse_blob_url(url: &ServoUrl) -> Result<(Uuid, ImmutableOrigin), &'stati
|
||||
|
||||
Ok((id, origin))
|
||||
}
|
||||
|
||||
/// This type upholds the variant that if the URL is a valid `blob` URL, then it has
|
||||
/// a token. Violating this invariant causes logic errors, but no unsafety.
|
||||
#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
|
||||
pub struct UrlWithBlobClaim {
|
||||
url: ServoUrl,
|
||||
token: Option<TokenSerializationGuard>,
|
||||
}
|
||||
|
||||
impl UrlWithBlobClaim {
|
||||
pub fn new(url: ServoUrl, token: Option<TokenSerializationGuard>) -> Self {
|
||||
debug_assert_eq!(parse_blob_url(&url).is_ok(), token.is_some());
|
||||
|
||||
Self { url, token }
|
||||
}
|
||||
|
||||
pub fn token(&self) -> Option<&BlobToken> {
|
||||
self.token.as_ref().map(|guard| guard.token.as_ref())
|
||||
}
|
||||
|
||||
pub fn blob_id(&self) -> Option<Uuid> {
|
||||
self.token.as_ref().map(|guard| guard.token.file_id)
|
||||
}
|
||||
|
||||
pub fn origin(&self) -> ImmutableOrigin {
|
||||
if let Some(guard) = self.token.as_ref() {
|
||||
return guard.token.origin.clone();
|
||||
}
|
||||
|
||||
self.url.origin()
|
||||
}
|
||||
|
||||
/// Constructs a [UrlWithBlobClaim] for URLs that are not `blob` URLs
|
||||
/// (Such URLs don't need to claim anything).
|
||||
///
|
||||
/// Returns an `Err` containing the original URL if it's a `blob` URL,
|
||||
/// so it can be reused without cloning.
|
||||
pub fn for_url(url: ServoUrl) -> Result<Self, ServoUrl> {
|
||||
if url.scheme() == "blob" {
|
||||
return Err(url);
|
||||
}
|
||||
|
||||
Ok(Self { url, token: None })
|
||||
}
|
||||
|
||||
/// This method should only exist temporarily, and all callers should either
|
||||
/// claim the blob or guarantee that the URL is not a `blob` URL.
|
||||
pub fn from_url_without_having_claimed_blob(url: ServoUrl) -> Self {
|
||||
if url.scheme() == "blob" {
|
||||
// See https://github.com/servo/servo/issues/25226 for more details
|
||||
log::warn!(
|
||||
"Creating blob URL without claiming its associated blob entry. This might cause race conditions if the URL is revoked."
|
||||
);
|
||||
}
|
||||
Self { url, token: None }
|
||||
}
|
||||
|
||||
pub fn url(&self) -> ServoUrl {
|
||||
self.url.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for UrlWithBlobClaim {
|
||||
type Target = ServoUrl;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.url
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for UrlWithBlobClaim {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.url
|
||||
}
|
||||
}
|
||||
|
||||
/// Guarantees that blob entries kept alive the contained token are not deallocated even
|
||||
/// if this token is serialized, dropped, and then later deserialized (possibly in a different thread).
|
||||
#[derive(Clone, Debug, MallocSizeOf)]
|
||||
pub struct TokenSerializationGuard {
|
||||
#[conditional_malloc_size_of]
|
||||
token: Arc<BlobToken>,
|
||||
}
|
||||
|
||||
impl serde::Serialize for TokenSerializationGuard {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let mut new_token = self.token.refresh();
|
||||
let result = new_token.serialize(serializer);
|
||||
if result.is_ok() {
|
||||
// This token belongs to whoever receives the serialized message, so don't free it.
|
||||
new_token.disown();
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> serde::Deserialize<'a> for TokenSerializationGuard {
|
||||
fn deserialize<D>(de: D) -> Result<Self, <D as serde::Deserializer<'a>>::Error>
|
||||
where
|
||||
D: serde::Deserializer<'a>,
|
||||
{
|
||||
struct TokenGuardVisitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for TokenGuardVisitor {
|
||||
type Value = TokenSerializationGuard;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(formatter, "a TokenSerializationGuard")
|
||||
}
|
||||
|
||||
fn visit_newtype_struct<D>(
|
||||
self,
|
||||
deserializer: D,
|
||||
) -> Result<Self::Value, <D as serde::Deserializer<'de>>::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
Ok(TokenSerializationGuard {
|
||||
token: Arc::new(BlobToken::deserialize(deserializer)?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
de.deserialize_newtype_struct("TokenSerializationGuard", TokenGuardVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, MallocSizeOf)]
|
||||
pub struct BlobResolver<'a> {
|
||||
pub origin: ImmutableOrigin,
|
||||
pub resource_threads: &'a ResourceThreads,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, MallocSizeOf, Serialize)]
|
||||
/// A reference to a blob URL that will revoke the blob when dropped,
|
||||
/// unless the `disown` method is invoked.
|
||||
pub struct BlobToken {
|
||||
pub token: Uuid,
|
||||
pub file_id: Uuid,
|
||||
pub disowned: bool,
|
||||
pub origin: ImmutableOrigin,
|
||||
// We need a mutex here because BlobTokens are shared among threads, and accessing
|
||||
// `GenericSender<CoreResourceMsg>` from different threads is not safe.
|
||||
//
|
||||
// We need a Arc because the Communicator is shared among different BlobTokens.
|
||||
#[conditional_malloc_size_of]
|
||||
pub communicator: Arc<Mutex<BlobTokenCommunicator>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, MallocSizeOf, Serialize)]
|
||||
pub struct BlobTokenCommunicator {
|
||||
pub revoke_sender: GenericSender<CoreResourceMsg>,
|
||||
pub refresh_token_sender: GenericSender<CoreResourceMsg>,
|
||||
}
|
||||
|
||||
impl BlobTokenCommunicator {
|
||||
pub fn stub_for_testing() -> Arc<Mutex<Self>> {
|
||||
Arc::new(Mutex::new(Self {
|
||||
revoke_sender: generic_channel::channel().unwrap().0,
|
||||
refresh_token_sender: generic_channel::channel().unwrap().0,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl BlobToken {
|
||||
fn refresh(&self) -> Self {
|
||||
let (new_token_sender, new_token_receiver) = generic_channel::channel().unwrap();
|
||||
let refresh_request = BlobTokenRefreshRequest {
|
||||
blob_id: self.file_id,
|
||||
new_token_sender,
|
||||
};
|
||||
self.communicator
|
||||
.lock()
|
||||
.refresh_token_sender
|
||||
.send(CoreResourceMsg::RefreshTokenForFile(refresh_request))
|
||||
.unwrap();
|
||||
let new_token = new_token_receiver.recv().unwrap();
|
||||
|
||||
BlobToken {
|
||||
token: new_token,
|
||||
file_id: self.file_id,
|
||||
communicator: self.communicator.clone(),
|
||||
disowned: false,
|
||||
origin: self.origin.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Prevents this token from revoking itself when it is dropped.
|
||||
fn disown(&mut self) {
|
||||
self.disowned = true;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> BlobResolver<'a> {
|
||||
pub fn acquire_blob_token_for(&self, url: &ServoUrl) -> Option<TokenSerializationGuard> {
|
||||
if url.scheme() != "blob" {
|
||||
return None;
|
||||
}
|
||||
let (file_id, origin) = parse_blob_url(url)
|
||||
.inspect_err(|error| log::warn!("Failed to acquire token for {url}: {error}"))
|
||||
.ok()?;
|
||||
let (sender, receiver) = generic_channel::channel().unwrap();
|
||||
self.resource_threads
|
||||
.send(CoreResourceMsg::ToFileManager(
|
||||
FileManagerThreadMsg::GetTokenForFile(file_id, origin, sender),
|
||||
))
|
||||
.ok()?;
|
||||
let reply = receiver.recv().ok()?;
|
||||
reply.token.map(|token_id| {
|
||||
let token = BlobToken {
|
||||
token: token_id,
|
||||
file_id,
|
||||
communicator: Arc::new(Mutex::new(BlobTokenCommunicator {
|
||||
revoke_sender: reply.revoke_sender,
|
||||
refresh_token_sender: reply.refresh_sender,
|
||||
})),
|
||||
disowned: false,
|
||||
origin: self.origin.clone(),
|
||||
};
|
||||
|
||||
TokenSerializationGuard {
|
||||
token: Arc::new(token),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for BlobToken {
|
||||
fn drop(&mut self) {
|
||||
if self.disowned {
|
||||
return;
|
||||
}
|
||||
|
||||
let revocation_request = BlobTokenRevocationRequest {
|
||||
token: self.token,
|
||||
blob_id: self.file_id,
|
||||
};
|
||||
let _ = self
|
||||
.communicator
|
||||
.lock()
|
||||
.revoke_sender
|
||||
.send(CoreResourceMsg::RevokeTokenForFile(revocation_request));
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for BlobToken {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("BlobToken")
|
||||
.field("token", &self.token)
|
||||
.field("file_id", &self.file_id)
|
||||
.field("disowned", &self.disowned)
|
||||
.field("origin", &self.origin)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,11 @@ use servo_base::generic_channel::GenericSender;
|
||||
use servo_url::ImmutableOrigin;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::CoreResourceMsg;
|
||||
use crate::blob_url_store::{BlobBuf, BlobURLStoreError};
|
||||
|
||||
/// A token modulating access to a file for a blob URL.
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum FileTokenCheck {
|
||||
/// Checking against a token not required,
|
||||
/// used for accessing a file
|
||||
@@ -166,6 +167,16 @@ pub enum FileManagerThreadMsg {
|
||||
ImmutableOrigin,
|
||||
GenericSender<Result<(), BlobURLStoreError>>,
|
||||
),
|
||||
|
||||
GetTokenForFile(Uuid, ImmutableOrigin, GenericSender<GetTokenForFileReply>),
|
||||
RevokeTokenForFile(Uuid, Uuid),
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct GetTokenForFileReply {
|
||||
pub token: Option<Uuid>,
|
||||
pub revoke_sender: GenericSender<CoreResourceMsg>,
|
||||
pub refresh_sender: GenericSender<CoreResourceMsg>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
|
||||
@@ -33,6 +33,7 @@ use servo_base::generic_channel::{
|
||||
};
|
||||
use servo_base::id::{CookieStoreId, HistoryStateId, PipelineId};
|
||||
use servo_url::{ImmutableOrigin, ServoUrl};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::fetch::headers::determine_nosniff;
|
||||
use crate::filemanager_thread::FileManagerThreadMsg;
|
||||
@@ -703,6 +704,20 @@ pub enum CoreResourceMsg {
|
||||
/// and exit
|
||||
Exit(GenericOneshotSender<()>),
|
||||
CollectMemoryReport(ReportsChan),
|
||||
RevokeTokenForFile(BlobTokenRevocationRequest),
|
||||
RefreshTokenForFile(BlobTokenRefreshRequest),
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
|
||||
pub struct BlobTokenRevocationRequest {
|
||||
pub blob_id: Uuid,
|
||||
pub token: Uuid,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
|
||||
pub struct BlobTokenRefreshRequest {
|
||||
pub blob_id: Uuid,
|
||||
pub new_token_sender: GenericSender<Uuid>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
|
||||
@@ -22,6 +22,7 @@ use tokio::sync::oneshot::Sender as TokioSender;
|
||||
use url::Position;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::blob_url_store::UrlWithBlobClaim;
|
||||
use crate::policy_container::{PolicyContainer, RequestPolicyContainer};
|
||||
use crate::pub_domains::is_same_site;
|
||||
use crate::response::{HttpsState, RedirectTaint, Response};
|
||||
@@ -149,7 +150,7 @@ pub struct PreloadKey {
|
||||
impl PreloadKey {
|
||||
pub fn new(request: &RequestBuilder) -> Self {
|
||||
Self {
|
||||
url: request.url.clone(),
|
||||
url: request.url.url(),
|
||||
destination: request.destination,
|
||||
mode: request.mode.clone(),
|
||||
credentials_mode: request.credentials_mode,
|
||||
@@ -429,7 +430,7 @@ pub struct RequestBuilder {
|
||||
pub method: Method,
|
||||
|
||||
/// <https://fetch.spec.whatwg.org/#concept-request-url>
|
||||
pub url: ServoUrl,
|
||||
pub url: UrlWithBlobClaim,
|
||||
|
||||
/// <https://fetch.spec.whatwg.org/#concept-request-header-list>
|
||||
#[serde(
|
||||
@@ -505,7 +506,11 @@ pub struct RequestBuilder {
|
||||
}
|
||||
|
||||
impl RequestBuilder {
|
||||
pub fn new(webview_id: Option<WebViewId>, url: ServoUrl, referrer: Referrer) -> RequestBuilder {
|
||||
pub fn new(
|
||||
webview_id: Option<WebViewId>,
|
||||
url: UrlWithBlobClaim,
|
||||
referrer: Referrer,
|
||||
) -> RequestBuilder {
|
||||
RequestBuilder {
|
||||
id: RequestId::default(),
|
||||
preload_id: None,
|
||||
@@ -744,7 +749,11 @@ impl RequestBuilder {
|
||||
request.cache_mode = self.cache_mode;
|
||||
request.referrer_policy = self.referrer_policy;
|
||||
request.redirect_mode = self.redirect_mode;
|
||||
let mut url_list = self.url_list;
|
||||
let mut url_list: Vec<_> = self
|
||||
.url_list
|
||||
.into_iter()
|
||||
.map(UrlWithBlobClaim::from_url_without_having_claimed_blob)
|
||||
.collect();
|
||||
if url_list.is_empty() {
|
||||
url_list.push(self.url);
|
||||
}
|
||||
@@ -830,7 +839,7 @@ pub struct Request {
|
||||
// Use the last method on url_list to act as spec current url field, and
|
||||
// first method to act as spec url field
|
||||
/// <https://fetch.spec.whatwg.org/#concept-request-url-list>
|
||||
pub url_list: Vec<ServoUrl>,
|
||||
pub url_list: Vec<UrlWithBlobClaim>,
|
||||
/// <https://fetch.spec.whatwg.org/#concept-request-redirect-count>
|
||||
pub redirect_count: u32,
|
||||
/// <https://fetch.spec.whatwg.org/#concept-request-response-tainting>
|
||||
@@ -850,7 +859,7 @@ pub struct Request {
|
||||
impl Request {
|
||||
pub fn new(
|
||||
id: RequestId,
|
||||
url: ServoUrl,
|
||||
url: UrlWithBlobClaim,
|
||||
origin: Option<Origin>,
|
||||
referrer: Referrer,
|
||||
pipeline_id: Option<PipelineId>,
|
||||
@@ -899,7 +908,7 @@ impl Request {
|
||||
|
||||
/// <https://fetch.spec.whatwg.org/#concept-request-url>
|
||||
pub fn url(&self) -> ServoUrl {
|
||||
self.url_list.first().unwrap().clone()
|
||||
self.url_list.first().unwrap().url()
|
||||
}
|
||||
|
||||
pub fn original_url(&self) -> ServoUrl {
|
||||
@@ -914,6 +923,11 @@ impl Request {
|
||||
|
||||
/// <https://fetch.spec.whatwg.org/#concept-request-current-url>
|
||||
pub fn current_url(&self) -> ServoUrl {
|
||||
self.current_url_with_blob_claim().url()
|
||||
}
|
||||
|
||||
/// <https://fetch.spec.whatwg.org/#concept-request-current-url>
|
||||
pub fn current_url_with_blob_claim(&self) -> UrlWithBlobClaim {
|
||||
self.url_list.last().unwrap().clone()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
[dedicated-worker-from-blob-url.window.html]
|
||||
[Creating a dedicated worker from a blob URL works immediately before revoking.]
|
||||
expected: FAIL
|
||||
Reference in New Issue
Block a user