use std::rc::Rc;
use std::str::FromStr;
use cssparser::match_ignore_ascii_case;
use dom_struct::dom_struct;
use http::Method as HttpMethod;
use http::header::{HeaderName, HeaderValue};
use http::method::InvalidMethod;
use js::rust::HandleObject;
use net_traits::ReferrerPolicy as MsgReferrerPolicy;
use net_traits::fetch::headers::is_forbidden_method;
use net_traits::request::{
CacheMode, CredentialsMode, Destination, Origin, RedirectMode, Referrer,
Request as NetTraitsRequest, RequestBuilder, RequestMode as NetTraitsRequestMode,
TraversableForUserPrompts,
};
use script_bindings::cformat;
use servo_url::ServoUrl;
use crate::body::{BodyMixin, BodyType, Extractable, clone_body_stream_for_dom_body, consume_body};
use crate::conversions::Convert;
use crate::dom::abortsignal::AbortSignal;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::HeadersBinding::{HeadersInit, HeadersMethods};
use crate::dom::bindings::codegen::Bindings::RequestBinding::{
ReferrerPolicy, RequestCache, RequestCredentials, RequestDestination, RequestInfo, RequestInit,
RequestMethods, RequestMode, RequestRedirect,
};
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto};
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::bindings::str::{ByteString, DOMString, USVString};
use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::globalscope::GlobalScope;
use crate::dom::headers::{Guard, Headers};
use crate::dom::promise::Promise;
use crate::dom::stream::readablestream::ReadableStream;
use crate::fetch::RequestWithGlobalScope;
use crate::script_runtime::CanGc;
use crate::url::ensure_blob_referenced_by_url_is_kept_alive;
#[dom_struct]
pub(crate) struct Request {
reflector_: Reflector,
#[no_trace]
request: DomRefCell<NetTraitsRequest>,
body_stream: MutNullableDom<ReadableStream>,
headers: MutNullableDom<Headers>,
signal: MutNullableDom<AbortSignal>,
}
impl Request {
fn new_inherited(global: &GlobalScope, url: ServoUrl) -> Request {
Request {
reflector_: Reflector::new(),
request: DomRefCell::new(net_request_from_global(global, url)),
body_stream: MutNullableDom::new(None),
headers: Default::default(),
signal: MutNullableDom::new(None),
}
}
fn new(
global: &GlobalScope,
proto: Option<HandleObject>,
url: ServoUrl,
can_gc: CanGc,
) -> DomRoot<Request> {
reflect_dom_object_with_proto(
Box::new(Request::new_inherited(global, url)),
global,
proto,
can_gc,
)
}
fn from_net_request(
global: &GlobalScope,
proto: Option<HandleObject>,
net_request: NetTraitsRequest,
can_gc: CanGc,
) -> DomRoot<Request> {
let r = Request::new(global, proto, net_request.current_url(), can_gc);
*r.request.borrow_mut() = net_request;
r
}
pub(crate) fn constructor(
cx: &mut js::context::JSContext,
global: &GlobalScope,
proto: Option<HandleObject>,
mut input: RequestInfo,
init: &RequestInit,
) -> Fallible<DomRoot<Request>> {
let temporary_request: NetTraitsRequest;
let mut fallback_mode: Option<NetTraitsRequestMode> = None;
let base_url = global.api_base_url();
let mut signal: Option<DomRoot<AbortSignal>> = None;
let mut input_body_is_unusable = false;
match input {
RequestInfo::USVString(USVString(ref usv_string)) => {
let parsed_url = base_url.join(usv_string);
if parsed_url.is_err() {
return Err(Error::Type(c"Url could not be parsed".to_owned()));
}
let url = parsed_url.unwrap();
if includes_credentials(&url) {
return Err(Error::Type(c"Url includes credentials".to_owned()));
}
temporary_request = net_request_from_global(global, url);
fallback_mode = Some(NetTraitsRequestMode::CorsMode);
},
RequestInfo::Request(ref input_request) => {
input_body_is_unusable = input_request.is_unusable();
temporary_request = input_request.request.borrow().clone();
signal = Some(input_request.Signal());
},
}
let origin = global.origin().immutable();
let mut traversable_for_user_prompts = TraversableForUserPrompts::Client;
if !init.window.handle().is_null_or_undefined() {
return Err(Error::Type(c"Window is present and is not null".to_owned()));
}
if !init.window.handle().is_undefined() {
traversable_for_user_prompts = TraversableForUserPrompts::NoTraversable;
}
let mut request: NetTraitsRequest;
request = net_request_from_global(global, temporary_request.current_url());
request.method = temporary_request.method;
request.headers = temporary_request.headers.clone();
request.unsafe_request = true;
request.traversable_for_user_prompts = traversable_for_user_prompts;
request.origin = Origin::Client;
request.referrer = temporary_request.referrer;
request.referrer_policy = temporary_request.referrer_policy;
request.mode = temporary_request.mode;
request.credentials_mode = temporary_request.credentials_mode;
request.cache_mode = temporary_request.cache_mode;
request.redirect_mode = temporary_request.redirect_mode;
request.integrity_metadata = temporary_request.integrity_metadata;
if init.body.is_some() ||
init.cache.is_some() ||
init.credentials.is_some() ||
init.integrity.is_some() ||
init.headers.is_some() ||
init.keepalive.is_some() ||
init.method.is_some() ||
init.mode.is_some() ||
init.redirect.is_some() ||
init.referrer.is_some() ||
init.referrerPolicy.is_some() ||
!init.window.handle().is_undefined()
{
if request.mode == NetTraitsRequestMode::Navigate {
request.mode = NetTraitsRequestMode::SameOrigin;
}
request.referrer = global.get_referrer();
request.referrer_policy = MsgReferrerPolicy::EmptyString;
}
if let Some(init_referrer) = init.referrer.as_ref() {
let referrer = &init_referrer.0;
if referrer.is_empty() {
request.referrer = Referrer::NoReferrer;
} else {
let parsed_referrer = base_url.join(referrer);
if parsed_referrer.is_err() {
return Err(Error::Type(c"Failed to parse referrer url".to_owned()));
}
if let Ok(parsed_referrer) = parsed_referrer {
if (parsed_referrer.cannot_be_a_base() &&
parsed_referrer.scheme() == "about" &&
parsed_referrer.path() == "client") ||
parsed_referrer.origin() != *origin
{
request.referrer = global.get_referrer();
} else {
request.referrer = Referrer::ReferrerUrl(parsed_referrer);
}
}
}
}
if let Some(init_referrerpolicy) = init.referrerPolicy.as_ref() {
let init_referrer_policy = (*init_referrerpolicy).convert();
request.referrer_policy = init_referrer_policy;
}
let mode = init.mode.as_ref().map(|m| (*m).convert()).or(fallback_mode);
if let Some(NetTraitsRequestMode::Navigate) = mode {
return Err(Error::Type(c"Request mode is Navigate".to_owned()));
}
if let Some(m) = mode {
request.mode = m;
}
if let Some(init_credentials) = init.credentials.as_ref() {
let credentials = (*init_credentials).convert();
request.credentials_mode = credentials;
}
if let Some(init_cache) = init.cache.as_ref() {
let cache = (*init_cache).convert();
request.cache_mode = cache;
}
if request.cache_mode == CacheMode::OnlyIfCached &&
request.mode != NetTraitsRequestMode::SameOrigin
{
return Err(Error::Type(
c"Cache is 'only-if-cached' and mode is not 'same-origin'".to_owned(),
));
}
if let Some(init_redirect) = init.redirect.as_ref() {
let redirect = (*init_redirect).convert();
request.redirect_mode = redirect;
}
if let Some(init_integrity) = init.integrity.as_ref() {
let integrity = init_integrity.clone().to_string();
request.integrity_metadata = integrity;
}
if let Some(init_keepalive) = init.keepalive {
request.keep_alive = init_keepalive;
}
if let Some(init_method) = init.method.as_ref() {
if !is_method(init_method) {
return Err(Error::Type(c"Method is not a method".to_owned()));
}
if is_forbidden_method(init_method) {
return Err(Error::Type(c"Method is forbidden".to_owned()));
}
let method = match init_method.as_str() {
Some(s) => normalize_method(s)
.map_err(|e| Error::Type(cformat!("Method is not valid: {:?}", e)))?,
None => return Err(Error::Type(c"Method is not a valid UTF8".to_owned())),
};
request.method = method;
}
if let Some(init_signal) = init.signal.as_ref() {
signal = init_signal.clone();
}
let r = Request::from_net_request(global, proto, request, CanGc::from_cx(cx));
let signals = signal.map_or(vec![], |s| vec![s]);
r.signal
.set(Some(&AbortSignal::create_dependent_abort_signal(
signals,
global,
CanGc::from_cx(cx),
)));
r.headers
.or_init(|| Headers::for_request(&r.global(), CanGc::from_cx(cx)));
let headers_copy = init
.headers
.as_ref()
.map(|possible_header| match possible_header {
HeadersInit::ByteStringSequenceSequence(init_sequence) => {
HeadersInit::ByteStringSequenceSequence(init_sequence.clone())
},
HeadersInit::ByteStringByteStringRecord(init_map) => {
HeadersInit::ByteStringByteStringRecord(init_map.clone())
},
});
if r.request.borrow().mode == NetTraitsRequestMode::NoCors {
let borrowed_request = r.request.borrow();
if !is_cors_safelisted_method(&borrowed_request.method) {
return Err(Error::Type(
c"The mode is 'no-cors' but the method is not a cors-safelisted method"
.to_owned(),
));
}
r.Headers(cx).set_guard(Guard::RequestNoCors);
}
match headers_copy {
None => {
if let RequestInfo::Request(ref input_request) = input {
r.Headers(cx).copy_from_headers(input_request.Headers(cx))?;
}
},
Some(headers_copy) => r.Headers(cx).fill(Some(headers_copy))?,
}
r.request.borrow_mut().headers = r.Headers(cx).get_headers_list();
let input_body = if let RequestInfo::Request(ref mut input_request) = input {
let mut input_request_request = input_request.request.borrow_mut();
r.body_stream.set(input_request.body().as_deref());
input_request_request.body.take()
} else {
None
};
if init.body.as_ref().is_some_and(|body| body.is_some()) || input_body.is_some() {
let req = r.request.borrow();
let req_method = &req.method;
match *req_method {
HttpMethod::GET => {
return Err(Error::Type(
c"Init's body is non-null, and request method is GET".to_owned(),
));
},
HttpMethod::HEAD => {
return Err(Error::Type(
c"Init's body is non-null, and request method is HEAD".to_owned(),
));
},
_ => {},
}
}
let mut init_body = None;
if let Some(Some(ref input_init_body)) = init.body {
let mut body_with_type =
input_init_body.extract(cx, global, r.request.borrow().keep_alive)?;
if let Some(contents) = body_with_type.content_type.take() {
let ct_header_name = b"Content-Type";
if !r
.Headers(cx)
.Has(ByteString::new(ct_header_name.to_vec()))
.unwrap()
{
let ct_header_val = contents.as_bytes();
r.Headers(cx).Append(
ByteString::new(ct_header_name.to_vec()),
ByteString::new(ct_header_val.to_vec()),
)?;
if let Ok(v) = HeaderValue::from_bytes(&ct_header_val) {
r.request
.borrow_mut()
.headers
.insert(HeaderName::from_bytes(ct_header_name).unwrap(), v);
}
}
}
let (net_body, stream) = body_with_type.into_net_request_body();
r.body_stream.set(Some(&*stream));
init_body = Some(net_body);
}
let final_body = init_body.or(input_body);
if final_body
.as_ref()
.is_some_and(|body| body.source_is_null())
{
let request_mode = &r.request.borrow().mode;
if *request_mode != NetTraitsRequestMode::CorsMode &&
*request_mode != NetTraitsRequestMode::SameOrigin
{
return Err(Error::Type(
c"Request mode must be Cors or SameOrigin".to_owned(),
));
}
}
if input_body_is_unusable {
return Err(Error::Type(c"Input body is unusable".to_owned()));
}
r.request.borrow_mut().body = final_body;
Ok(r)
}
fn clone_from(cx: &mut js::context::JSContext, r: &Request) -> Fallible<DomRoot<Request>> {
let req = r.request.borrow();
let url = req.url();
let headers_guard = r.Headers(cx).get_guard();
let mut new_req_inner = req.clone();
let body = new_req_inner.body.take();
let r_clone = Request::new(&r.global(), None, url, CanGc::from_cx(cx));
*r_clone.request.borrow_mut() = new_req_inner;
if let Some(body) = body {
r_clone.request.borrow_mut().body = Some(body);
}
r_clone.Headers(cx).copy_from_headers(r.Headers(cx))?;
r_clone.Headers(cx).set_guard(headers_guard);
clone_body_stream_for_dom_body(cx, &r.body_stream, &r_clone.body_stream)?;
Ok(r_clone)
}
pub(crate) fn get_request(&self) -> NetTraitsRequest {
self.request.borrow().clone()
}
}
fn net_request_from_global(global: &GlobalScope, url: ServoUrl) -> NetTraitsRequest {
let url = ensure_blob_referenced_by_url_is_kept_alive(global, url);
RequestBuilder::new(global.webview_id(), url, global.get_referrer())
.with_global_scope(global)
.build()
}
fn normalize_method(m: &str) -> Result<HttpMethod, InvalidMethod> {
match_ignore_ascii_case! { m,
"delete" => return Ok(HttpMethod::DELETE),
"get" => return Ok(HttpMethod::GET),
"head" => return Ok(HttpMethod::HEAD),
"options" => return Ok(HttpMethod::OPTIONS),
"post" => return Ok(HttpMethod::POST),
"put" => return Ok(HttpMethod::PUT),
_ => (),
}
debug!("Method: {:?}", m);
HttpMethod::from_str(m)
}
fn is_method(m: &ByteString) -> bool {
m.as_str().is_some()
}
fn is_cors_safelisted_method(m: &HttpMethod) -> bool {
m == HttpMethod::GET || m == HttpMethod::HEAD || m == HttpMethod::POST
}
fn includes_credentials(input: &ServoUrl) -> bool {
!input.username().is_empty() || input.password().is_some()
}
impl RequestMethods<crate::DomTypeHolder> for Request {
fn Constructor(
cx: &mut js::context::JSContext,
global: &GlobalScope,
proto: Option<HandleObject>,
input: RequestInfo,
init: RootedTraceableBox<RequestInit>,
) -> Fallible<DomRoot<Request>> {
Self::constructor(cx, global, proto, input, &init)
}
fn Method(&self) -> ByteString {
let r = self.request.borrow();
ByteString::new(r.method.as_ref().as_bytes().into())
}
fn Url(&self) -> USVString {
let r = self.request.borrow();
USVString(r.url_list.first().map_or("", |u| u.as_str()).into())
}
fn Headers(&self, cx: &mut js::context::JSContext) -> DomRoot<Headers> {
self.headers
.or_init(|| Headers::new(&self.global(), CanGc::from_cx(cx)))
}
fn Destination(&self) -> RequestDestination {
self.request.borrow().destination.convert()
}
fn Referrer(&self) -> USVString {
let r = self.request.borrow();
USVString(match r.referrer {
Referrer::NoReferrer => String::from(""),
Referrer::Client(_) => String::from("about:client"),
Referrer::ReferrerUrl(ref u) => {
let u_c = u.clone();
u_c.into_string()
},
})
}
fn ReferrerPolicy(&self) -> ReferrerPolicy {
self.request.borrow().referrer_policy.convert()
}
fn Mode(&self) -> RequestMode {
self.request.borrow().mode.clone().convert()
}
fn Credentials(&self) -> RequestCredentials {
let r = self.request.borrow().clone();
r.credentials_mode.convert()
}
fn Cache(&self) -> RequestCache {
let r = self.request.borrow().clone();
r.cache_mode.convert()
}
fn Redirect(&self) -> RequestRedirect {
let r = self.request.borrow().clone();
r.redirect_mode.convert()
}
fn Integrity(&self) -> DOMString {
self.request.borrow().integrity_metadata.clone().into()
}
fn Keepalive(&self) -> bool {
self.request.borrow().keep_alive
}
fn GetBody(&self) -> Option<DomRoot<ReadableStream>> {
self.body()
}
fn BodyUsed(&self) -> bool {
self.is_body_used()
}
fn Signal(&self) -> DomRoot<AbortSignal> {
self.signal
.get()
.expect("Should always be initialized in constructor and clone")
}
fn Clone(&self, cx: &mut js::context::JSContext) -> Fallible<DomRoot<Request>> {
if self.is_unusable() {
return Err(Error::Type(c"Request is unusable".to_owned()));
}
let cloned_request = Request::clone_from(cx, self)?;
let signal = self.signal.get().expect("Should always be initialized");
let cloned_signal = AbortSignal::create_dependent_abort_signal(
vec![signal],
&self.global(),
CanGc::from_cx(cx),
);
cloned_request.signal.set(Some(&cloned_signal));
Ok(cloned_request)
}
fn Text(&self, cx: &mut js::context::JSContext) -> Rc<Promise> {
consume_body(cx, self, BodyType::Text)
}
fn Blob(&self, cx: &mut js::context::JSContext) -> Rc<Promise> {
consume_body(cx, self, BodyType::Blob)
}
fn FormData(&self, cx: &mut js::context::JSContext) -> Rc<Promise> {
consume_body(cx, self, BodyType::FormData)
}
fn Json(&self, cx: &mut js::context::JSContext) -> Rc<Promise> {
consume_body(cx, self, BodyType::Json)
}
fn ArrayBuffer(&self, cx: &mut js::context::JSContext) -> Rc<Promise> {
consume_body(cx, self, BodyType::ArrayBuffer)
}
fn Bytes(&self, cx: &mut js::context::JSContext) -> Rc<Promise> {
consume_body(cx, self, BodyType::Bytes)
}
}
impl BodyMixin for Request {
fn is_body_used(&self) -> bool {
let body_stream = self.body_stream.get();
body_stream
.as_ref()
.is_some_and(|stream| stream.is_disturbed())
}
fn is_unusable(&self) -> bool {
let body_stream = self.body_stream.get();
body_stream
.as_ref()
.is_some_and(|stream| stream.is_disturbed() || stream.is_locked())
}
fn body(&self) -> Option<DomRoot<ReadableStream>> {
self.body_stream.get()
}
fn get_mime_type(&self, cx: &mut js::context::JSContext) -> Vec<u8> {
let headers = self.Headers(cx);
headers.extract_mime_type()
}
}
impl Convert<CacheMode> for RequestCache {
fn convert(self) -> CacheMode {
match self {
RequestCache::Default => CacheMode::Default,
RequestCache::No_store => CacheMode::NoStore,
RequestCache::Reload => CacheMode::Reload,
RequestCache::No_cache => CacheMode::NoCache,
RequestCache::Force_cache => CacheMode::ForceCache,
RequestCache::Only_if_cached => CacheMode::OnlyIfCached,
}
}
}
impl Convert<RequestCache> for CacheMode {
fn convert(self) -> RequestCache {
match self {
CacheMode::Default => RequestCache::Default,
CacheMode::NoStore => RequestCache::No_store,
CacheMode::Reload => RequestCache::Reload,
CacheMode::NoCache => RequestCache::No_cache,
CacheMode::ForceCache => RequestCache::Force_cache,
CacheMode::OnlyIfCached => RequestCache::Only_if_cached,
}
}
}
impl Convert<CredentialsMode> for RequestCredentials {
fn convert(self) -> CredentialsMode {
match self {
RequestCredentials::Omit => CredentialsMode::Omit,
RequestCredentials::Same_origin => CredentialsMode::CredentialsSameOrigin,
RequestCredentials::Include => CredentialsMode::Include,
}
}
}
impl Convert<RequestCredentials> for CredentialsMode {
fn convert(self) -> RequestCredentials {
match self {
CredentialsMode::Omit => RequestCredentials::Omit,
CredentialsMode::CredentialsSameOrigin => RequestCredentials::Same_origin,
CredentialsMode::Include => RequestCredentials::Include,
}
}
}
impl Convert<Destination> for RequestDestination {
fn convert(self) -> Destination {
match self {
RequestDestination::_empty => Destination::None,
RequestDestination::Audio => Destination::Audio,
RequestDestination::Document => Destination::Document,
RequestDestination::Embed => Destination::Embed,
RequestDestination::Font => Destination::Font,
RequestDestination::Frame => Destination::Frame,
RequestDestination::Iframe => Destination::IFrame,
RequestDestination::Image => Destination::Image,
RequestDestination::Manifest => Destination::Manifest,
RequestDestination::Json => Destination::Json,
RequestDestination::Object => Destination::Object,
RequestDestination::Report => Destination::Report,
RequestDestination::Script => Destination::Script,
RequestDestination::Sharedworker => Destination::SharedWorker,
RequestDestination::Style => Destination::Style,
RequestDestination::Track => Destination::Track,
RequestDestination::Video => Destination::Video,
RequestDestination::Worker => Destination::Worker,
RequestDestination::Xslt => Destination::Xslt,
}
}
}
impl Convert<RequestDestination> for Destination {
fn convert(self) -> RequestDestination {
match self {
Destination::None => RequestDestination::_empty,
Destination::Audio => RequestDestination::Audio,
Destination::Document => RequestDestination::Document,
Destination::Embed => RequestDestination::Embed,
Destination::Font => RequestDestination::Font,
Destination::Frame => RequestDestination::Frame,
Destination::IFrame => RequestDestination::Iframe,
Destination::Image => RequestDestination::Image,
Destination::Manifest => RequestDestination::Manifest,
Destination::Json => RequestDestination::Json,
Destination::Object => RequestDestination::Object,
Destination::Report => RequestDestination::Report,
Destination::Script => RequestDestination::Script,
Destination::ServiceWorker | Destination::AudioWorklet | Destination::PaintWorklet => {
panic!("ServiceWorker request destination should not be exposed to DOM")
},
Destination::SharedWorker => RequestDestination::Sharedworker,
Destination::Style => RequestDestination::Style,
Destination::Track => RequestDestination::Track,
Destination::Video => RequestDestination::Video,
Destination::Worker => RequestDestination::Worker,
Destination::Xslt => RequestDestination::Xslt,
Destination::WebIdentity => RequestDestination::_empty,
}
}
}
impl Convert<NetTraitsRequestMode> for RequestMode {
fn convert(self) -> NetTraitsRequestMode {
match self {
RequestMode::Navigate => NetTraitsRequestMode::Navigate,
RequestMode::Same_origin => NetTraitsRequestMode::SameOrigin,
RequestMode::No_cors => NetTraitsRequestMode::NoCors,
RequestMode::Cors => NetTraitsRequestMode::CorsMode,
}
}
}
impl Convert<RequestMode> for NetTraitsRequestMode {
fn convert(self) -> RequestMode {
match self {
NetTraitsRequestMode::Navigate => RequestMode::Navigate,
NetTraitsRequestMode::SameOrigin => RequestMode::Same_origin,
NetTraitsRequestMode::NoCors => RequestMode::No_cors,
NetTraitsRequestMode::CorsMode => RequestMode::Cors,
NetTraitsRequestMode::WebSocket { .. } => {
unreachable!("Websocket request mode should never be exposed to Dom")
},
}
}
}
impl Convert<MsgReferrerPolicy> for ReferrerPolicy {
fn convert(self) -> MsgReferrerPolicy {
match self {
ReferrerPolicy::_empty => MsgReferrerPolicy::EmptyString,
ReferrerPolicy::No_referrer => MsgReferrerPolicy::NoReferrer,
ReferrerPolicy::No_referrer_when_downgrade => {
MsgReferrerPolicy::NoReferrerWhenDowngrade
},
ReferrerPolicy::Origin => MsgReferrerPolicy::Origin,
ReferrerPolicy::Origin_when_cross_origin => MsgReferrerPolicy::OriginWhenCrossOrigin,
ReferrerPolicy::Unsafe_url => MsgReferrerPolicy::UnsafeUrl,
ReferrerPolicy::Same_origin => MsgReferrerPolicy::SameOrigin,
ReferrerPolicy::Strict_origin => MsgReferrerPolicy::StrictOrigin,
ReferrerPolicy::Strict_origin_when_cross_origin => {
MsgReferrerPolicy::StrictOriginWhenCrossOrigin
},
}
}
}
impl Convert<ReferrerPolicy> for MsgReferrerPolicy {
fn convert(self) -> ReferrerPolicy {
match self {
MsgReferrerPolicy::EmptyString => ReferrerPolicy::_empty,
MsgReferrerPolicy::NoReferrer => ReferrerPolicy::No_referrer,
MsgReferrerPolicy::NoReferrerWhenDowngrade => {
ReferrerPolicy::No_referrer_when_downgrade
},
MsgReferrerPolicy::Origin => ReferrerPolicy::Origin,
MsgReferrerPolicy::OriginWhenCrossOrigin => ReferrerPolicy::Origin_when_cross_origin,
MsgReferrerPolicy::UnsafeUrl => ReferrerPolicy::Unsafe_url,
MsgReferrerPolicy::SameOrigin => ReferrerPolicy::Same_origin,
MsgReferrerPolicy::StrictOrigin => ReferrerPolicy::Strict_origin,
MsgReferrerPolicy::StrictOriginWhenCrossOrigin => {
ReferrerPolicy::Strict_origin_when_cross_origin
},
}
}
}
impl Convert<RedirectMode> for RequestRedirect {
fn convert(self) -> RedirectMode {
match self {
RequestRedirect::Follow => RedirectMode::Follow,
RequestRedirect::Error => RedirectMode::Error,
RequestRedirect::Manual => RedirectMode::Manual,
}
}
}
impl Convert<RequestRedirect> for RedirectMode {
fn convert(self) -> RequestRedirect {
match self {
RedirectMode::Follow => RequestRedirect::Follow,
RedirectMode::Error => RequestRedirect::Error,
RedirectMode::Manual => RequestRedirect::Manual,
}
}
}