Implement basics of link preloading (#37036)

These changes allow a minimal set of checks for font-src
CSP checks to pass.

Part of #4577
Part of #35035

---------

Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
This commit is contained in:
Tim van der Lippe 2025-05-29 13:26:27 +02:00 committed by GitHub
parent 9dc1391bef
commit 36e4886da1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
174 changed files with 2814 additions and 1097 deletions

View file

@ -16,7 +16,6 @@ pub mod http_cache;
pub mod http_loader;
pub mod image_cache;
pub mod local_directory_listing;
pub mod mime_classifier;
pub mod protocols;
pub mod request_interceptor;
pub mod resource_thread;

View file

@ -14,7 +14,6 @@ mod filemanager_thread;
mod hsts;
mod http_cache;
mod http_loader;
mod mime_classifier;
mod resource_thread;
mod subresource_integrity;

View file

@ -13,7 +13,7 @@ use std::str::FromStr;
use std::{fmt, mem};
use content_security_policy as csp;
use cssparser::match_ignore_ascii_case;
use cssparser::{Parser as CssParser, ParserInput as CssParserInput, match_ignore_ascii_case};
use devtools_traits::AttrInfo;
use dom_struct::dom_struct;
use embedder_traits::InputMethodType;
@ -36,6 +36,8 @@ use style::applicable_declarations::ApplicableDeclarationBlock;
use style::attr::{AttrValue, LengthOrPercentageOrAuto};
use style::context::QuirksMode;
use style::invalidation::element::restyle_hints::RestyleHint;
use style::media_queries::MediaList;
use style::parser::ParserContext as CssParserContext;
use style::properties::longhands::{
self, background_image, border_spacing, font_family, font_size,
};
@ -50,13 +52,14 @@ use style::selector_parser::{
};
use style::shared_lock::{Locked, SharedRwLock};
use style::stylesheets::layer_rule::LayerOrder;
use style::stylesheets::{CssRuleType, UrlExtraData};
use style::stylesheets::{CssRuleType, Origin as CssOrigin, UrlExtraData};
use style::values::computed::Overflow;
use style::values::generics::NonNegative;
use style::values::generics::position::PreferredRatio;
use style::values::generics::ratio::Ratio;
use style::values::{AtomIdent, AtomString, CSSFloat, computed, specified};
use style::{ArcSlice, CaseSensitivityExt, dom_apis, thread_state};
use style_traits::ParsingMode as CssParsingMode;
use stylo_atoms::Atom;
use stylo_dom::ElementState;
use xml5ever::serialize::TraversalScope::{
@ -781,6 +784,33 @@ impl Element {
.registered_intersection_observers
.retain(|reg_obs| *reg_obs.observer != *observer)
}
/// <https://html.spec.whatwg.org/multipage/#matches-the-environment>
pub(crate) fn matches_environment(&self, media_query: &str) -> bool {
let document = self.owner_document();
let quirks_mode = document.quirks_mode();
let document_url_data = UrlExtraData(document.url().get_arc());
// FIXME(emilio): This should do the same that we do for other media
// lists regarding the rule type and such, though it doesn't really
// matter right now...
//
// Also, ParsingMode::all() is wrong, and should be DEFAULT.
let context = CssParserContext::new(
CssOrigin::Author,
&document_url_data,
Some(CssRuleType::Style),
CssParsingMode::all(),
quirks_mode,
/* namespaces = */ Default::default(),
None,
None,
);
let mut parser_input = CssParserInput::new(media_query);
let mut parser = CssParser::new(&mut parser_input);
let media_list = MediaList::parse(&context, &mut parser);
let result = media_list.evaluate(document.window().layout().device(), quirks_mode);
result
}
}
/// <https://dom.spec.whatwg.org/#valid-shadow-host-name>

View file

@ -34,9 +34,8 @@ use servo_url::ServoUrl;
use servo_url::origin::MutableOrigin;
use style::attr::{AttrValue, LengthOrPercentageOrAuto, parse_integer, parse_length};
use style::context::QuirksMode;
use style::media_queries::MediaList;
use style::parser::ParserContext;
use style::stylesheets::{CssRuleType, Origin, UrlExtraData};
use style::stylesheets::{CssRuleType, Origin};
use style::values::specified::AbsoluteLength;
use style::values::specified::length::{Length, NoCalcLength};
use style::values::specified::source_size_list::SourceSizeList;
@ -678,7 +677,7 @@ impl HTMLImageElement {
// Step 4.6
if let Some(x) = element.get_attribute(&ns!(), &local_name!("media")) {
if !self.matches_environment(x.value().to_string()) {
if !elem.matches_environment(&x.value()) {
continue;
}
}
@ -722,33 +721,6 @@ impl HTMLImageElement {
result
}
/// <https://html.spec.whatwg.org/multipage/#matches-the-environment>
fn matches_environment(&self, media_query: String) -> bool {
let document = self.owner_document();
let quirks_mode = document.quirks_mode();
let document_url_data = UrlExtraData(document.url().get_arc());
// FIXME(emilio): This should do the same that we do for other media
// lists regarding the rule type and such, though it doesn't really
// matter right now...
//
// Also, ParsingMode::all() is wrong, and should be DEFAULT.
let context = ParserContext::new(
Origin::Author,
&document_url_data,
Some(CssRuleType::Style),
ParsingMode::all(),
quirks_mode,
/* namespaces = */ Default::default(),
None,
None,
);
let mut parserInput = ParserInput::new(&media_query);
let mut parser = Parser::new(&mut parserInput);
let media_list = MediaList::parse(&context, &mut parser);
let result = media_list.evaluate(document.window().layout().device(), quirks_mode);
result
}
/// <https://html.spec.whatwg.org/multipage/#normalise-the-source-densities>
fn normalise_source_densities(&self, source_set: &mut SourceSet, width: Option<Length>) {
// Step 1

View file

@ -5,6 +5,7 @@
use std::borrow::{Borrow, ToOwned};
use std::cell::Cell;
use std::default::Default;
use std::str::FromStr;
use base::id::WebViewId;
use content_security_policy as csp;
@ -12,6 +13,8 @@ use dom_struct::dom_struct;
use embedder_traits::EmbedderMsg;
use html5ever::{LocalName, Prefix, local_name, ns};
use js::rust::HandleObject;
use mime::Mime;
use net_traits::mime_classifier::{MediaType, MimeClassifier};
use net_traits::policy_container::PolicyContainer;
use net_traits::request::{
CorsSettings, Destination, Initiator, InsecureRequestsPolicy, Referrer, RequestBuilder,
@ -22,7 +25,7 @@ use net_traits::{
ResourceTimingType,
};
use servo_arc::Arc;
use servo_url::ServoUrl;
use servo_url::{ImmutableOrigin, ServoUrl};
use style::attr::AttrValue;
use style::stylesheets::Stylesheet;
use stylo_atoms::Atom;
@ -78,6 +81,7 @@ struct LinkProcessingOptions {
policy_container: PolicyContainer,
source_set: Option<()>,
base_url: ServoUrl,
origin: ImmutableOrigin,
insecure_requests_policy: InsecureRequestsPolicy,
has_trustworthy_ancestor_origin: bool,
// Some fields that we don't need yet are missing
@ -113,6 +117,10 @@ pub(crate) struct HTMLLinkElement {
request_generation_id: Cell<RequestGenerationId>,
/// <https://html.spec.whatwg.org/multipage/#explicitly-enabled>
is_explicitly_enabled: Cell<bool>,
/// Whether the previous type matched with the destination
previous_type_matched: Cell<bool>,
/// Whether the previous media environment matched with the media query
previous_media_environment_matched: Cell<bool>,
}
impl HTMLLinkElement {
@ -133,6 +141,8 @@ impl HTMLLinkElement {
any_failed_load: Cell::new(false),
request_generation_id: Cell::new(RequestGenerationId(0)),
is_explicitly_enabled: Cell::new(false),
previous_type_matched: Cell::new(true),
previous_media_environment_matched: Cell::new(true),
}
}
@ -236,7 +246,7 @@ impl VirtualMethods for HTMLLinkElement {
return;
}
if !self.upcast::<Node>().is_connected() || is_removal {
if !self.upcast::<Node>().is_connected() {
return;
}
match *local_name {
@ -245,6 +255,12 @@ impl VirtualMethods for HTMLLinkElement {
.set(LinkRelations::for_element(self.upcast()));
},
local_name!("href") => {
if is_removal {
return;
}
// https://html.spec.whatwg.org/multipage/#link-type-stylesheet
// When the href attribute of the link element of an external resource link
// that is already browsing-context connected is changed.
if self.relations.get().contains(LinkRelations::STYLESHEET) {
self.handle_stylesheet_url(&attr.value());
}
@ -254,9 +270,19 @@ impl VirtualMethods for HTMLLinkElement {
self.handle_favicon_url(&attr.value(), &sizes);
}
// https://html.spec.whatwg.org/multipage/#link-type-prefetch
// When the href attribute of the link element of an external resource link
// that is already browsing-context connected is changed.
if self.relations.get().contains(LinkRelations::PREFETCH) {
self.fetch_and_process_prefetch_link(&attr.value());
}
// https://html.spec.whatwg.org/multipage/#link-type-preload
// When the href attribute of the link element of an external resource link
// that is already browsing-context connected is changed.
if self.relations.get().contains(LinkRelations::PRELOAD) {
self.handle_preload_url();
}
},
local_name!("sizes") if self.relations.get().contains(LinkRelations::ICON) => {
if let Some(ref href) = get_attr(self.upcast(), &local_name!("href")) {
@ -264,9 +290,73 @@ impl VirtualMethods for HTMLLinkElement {
}
},
local_name!("crossorigin") => {
// https://html.spec.whatwg.org/multipage/#link-type-prefetch
// When the crossorigin attribute of the link element of an external resource link
// that is already browsing-context connected is set, changed, or removed.
if self.relations.get().contains(LinkRelations::PREFETCH) {
self.fetch_and_process_prefetch_link(&attr.value());
}
// https://html.spec.whatwg.org/multipage/#link-type-stylesheet
// When the crossorigin attribute of the link element of an external resource link
// that is already browsing-context connected is set, changed, or removed.
if self.relations.get().contains(LinkRelations::STYLESHEET) {
self.handle_stylesheet_url(&attr.value());
}
},
local_name!("as") => {
// https://html.spec.whatwg.org/multipage/#link-type-preload
// When the as attribute of the link element of an external resource link
// that is already browsing-context connected is changed.
if self.relations.get().contains(LinkRelations::PRELOAD) {
if let AttributeMutation::Set(Some(_)) = mutation {
self.handle_preload_url();
}
}
},
local_name!("type") => {
// https://html.spec.whatwg.org/multipage/#link-type-stylesheet
// When the type attribute of the link element of an external resource link that
// is already browsing-context connected is set or changed to a value that does
// not or no longer matches the Content-Type metadata of the previous obtained
// external resource, if any.
//
// TODO: Match Content-Type metadata to check if it needs to be updated
if self.relations.get().contains(LinkRelations::STYLESHEET) {
self.handle_stylesheet_url(&attr.value());
}
// https://html.spec.whatwg.org/multipage/#link-type-preload
// When the type attribute of the link element of an external resource link that
// is already browsing-context connected, but was previously not obtained due to
// the type attribute specifying an unsupported type for the request destination,
// is set, removed, or changed.
if self.relations.get().contains(LinkRelations::PRELOAD) &&
!self.previous_type_matched.get()
{
self.handle_preload_url();
}
},
local_name!("media") => {
// https://html.spec.whatwg.org/multipage/#link-type-preload
// When the media attribute of the link element of an external resource link that
// is already browsing-context connected, but was previously not obtained due to
// the media attribute not matching the environment, is changed or removed.
if self.relations.get().contains(LinkRelations::PRELOAD) &&
!self.previous_media_environment_matched.get()
{
match mutation {
AttributeMutation::Removed | AttributeMutation::Set(Some(_)) => {
self.handle_preload_url()
},
_ => {},
};
}
let matches_media_environment =
self.upcast::<Element>().matches_environment(&attr.value());
self.previous_media_environment_matched
.set(matches_media_environment);
},
_ => {},
}
@ -307,6 +397,10 @@ impl VirtualMethods for HTMLLinkElement {
if relations.contains(LinkRelations::PREFETCH) {
self.fetch_and_process_prefetch_link(&href);
}
if relations.contains(LinkRelations::PRELOAD) {
self.handle_preload_url();
}
}
}
}
@ -325,6 +419,14 @@ impl VirtualMethods for HTMLLinkElement {
}
impl HTMLLinkElement {
fn compute_destination_for_attribute(&self) -> Destination {
let element = self.upcast::<Element>();
element
.get_attribute(&ns!(), &local_name!("as"))
.map(|attr| translate_a_preload_destination(&attr.value()))
.unwrap_or(Destination::None)
}
/// <https://html.spec.whatwg.org/multipage/#create-link-options-from-element>
fn processing_options(&self) -> LinkProcessingOptions {
let element = self.upcast::<Element>();
@ -333,10 +435,7 @@ impl HTMLLinkElement {
let document = self.upcast::<Node>().owner_doc();
// Step 2. Let options be a new link processing options
let destination = element
.get_attribute(&ns!(), &local_name!("as"))
.map(|attr| translate_a_preload_destination(&attr.value()))
.unwrap_or(Destination::None);
let destination = self.compute_destination_for_attribute();
let mut options = LinkProcessingOptions {
href: String::new(),
@ -348,6 +447,7 @@ impl HTMLLinkElement {
referrer_policy: referrer_policy_for_element(element),
policy_container: document.policy_container().to_owned(),
source_set: None, // FIXME
origin: document.borrow().origin().immutable().to_owned(),
base_url: document.borrow().base_url(),
insecure_requests_policy: document.insecure_requests_policy(),
has_trustworthy_ancestor_origin: document.has_trustworthy_ancestor_or_current_origin(),
@ -446,6 +546,10 @@ impl HTMLLinkElement {
None => "",
};
if !element.matches_environment(mq_str) {
return;
}
let media = MediaList::parse_media_list(mq_str, document.window());
let im_attribute = element.get_attribute(&ns!(), &local_name!("integrity"));
@ -458,8 +562,6 @@ impl HTMLLinkElement {
self.request_generation_id
.set(self.request_generation_id.get().increment());
// TODO: #8085 - Don't load external stylesheets if the node's mq
// doesn't match.
let loader = StylesheetLoader::for_element(self.upcast());
loader.load(
StylesheetContextSource::LinkElement { media: Some(media) },
@ -494,6 +596,133 @@ impl HTMLLinkElement {
Err(e) => debug!("Parsing url {} failed: {}", href, e),
}
}
/// <https://html.spec.whatwg.org/multipage/#link-type-preload:fetch-and-process-the-linked-resource-2>
fn handle_preload_url(&self) {
// Step 1. Update the source set for el.
// TODO
// Step 2. Let options be the result of creating link options from el.
let options = self.processing_options();
// Step 3. Preload options, with the following steps given a response response:
// Step 3.1 If response is a network error, fire an event named error at el.
// Otherwise, fire an event named load at el.
self.preload(options);
}
/// <https://html.spec.whatwg.org/multipage/#preload>
fn preload(&self, options: LinkProcessingOptions) {
// Step 1. If options's type doesn't match options's destination, then return.
let type_matches_destination: bool =
HTMLLinkElement::type_matches_destination(&options.link_type, options.destination);
self.previous_type_matched.set(type_matches_destination);
if !type_matches_destination {
return;
}
// Step 2. If options's destination is "image" and options's source set is not null,
// then set options's href to the result of selecting an image source from options's source set.
// TODO
// Step 3. Let request be the result of creating a link request given options.
let url = options.base_url.clone();
let Some(request) = options.create_link_request(self.owner_window().webview_id()) else {
// Step 4. If request is null, then return.
return;
};
let document = self.upcast::<Node>().owner_doc();
// Step 5. Let unsafeEndTime be 0.
// TODO
// Step 6. Let entry be a new preload entry whose integrity metadata is options's integrity.
// TODO
// Step 7. Let key be the result of creating a preload key given request.
// TODO
// Step 8. If options's document is "pending", then set request's initiator type to "early hint".
// TODO
// 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.
// Step 11. Set controller to the result of fetching request, with processResponseConsumeBody
// set to the following steps given a response response and null, failure, or a byte sequence bodyBytes:
let fetch_context = PreloadContext {
url,
link: Trusted::new(self),
resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource),
};
document.fetch_background(request.clone(), fetch_context);
}
/// <https://html.spec.whatwg.org/multipage/#match-preload-type>
fn type_matches_destination(mime_type: &str, destination: Option<Destination>) -> bool {
// Step 1. If type is an empty string, then return true.
if mime_type.is_empty() {
return true;
}
// Step 2. If destination is "fetch", then return true.
//
// Fetch is handled as an empty string destination in the spec:
// https://fetch.spec.whatwg.org/#concept-potential-destination-translate
let Some(destination) = destination else {
return false;
};
if destination == Destination::None {
return true;
}
// Step 3. Let mimeTypeRecord be the result of parsing type.
let Ok(mime_type_record) = Mime::from_str(mime_type) else {
// Step 4. If mimeTypeRecord is failure, then return false.
return false;
};
// Step 5. If mimeTypeRecord is not supported by the user agent, then return false.
//
// We currently don't check if we actually support the mime type. Only if we can classify
// it according to the spec.
let Some(mime_type) = MimeClassifier::get_media_type(&mime_type_record) else {
return false;
};
// Step 6. If any of the following are true:
if
// destination is "audio" or "video", and mimeTypeRecord is an audio or video MIME type;
((destination == Destination::Audio || destination == Destination::Video) &&
mime_type == MediaType::AudioVideo)
// destination is a script-like destination and mimeTypeRecord is a JavaScript MIME type;
|| (destination.is_script_like() && mime_type == MediaType::JavaScript)
// destination is "image" and mimeTypeRecord is an image MIME type;
|| (destination == Destination::Image && mime_type == MediaType::Image)
// destination is "font" and mimeTypeRecord is a font MIME type;
|| (destination == Destination::Font && mime_type == MediaType::Font)
// destination is "json" and mimeTypeRecord is a JSON MIME type;
|| (destination == Destination::Json && mime_type == MediaType::Json)
// destination is "style" and mimeTypeRecord's essence is text/css; or
|| (destination == Destination::Style && mime_type_record == mime::TEXT_CSS)
// destination is "track" and mimeTypeRecord's essence is text/vtt,
|| (destination == Destination::Track && mime_type_record.essence_str() == "text/vtt")
{
// then return true.
return true;
}
// Step 7. Return false.
false
}
fn fire_event_after_response(&self, response: Result<ResourceFetchTiming, NetworkError>) {
if response.is_err() {
self.upcast::<EventTarget>()
.fire_event(atom!("error"), CanGc::note());
} else {
// TODO(35035): Figure out why we need to queue a task for the load event. Otherwise
// the performance timing data hasn't been saved yet, which fails several preload
// WPT tests that assume that performance timing information is available when
// the load event is fired.
let this = Trusted::new(self);
self.owner_global()
.task_manager()
.performance_timeline_task_source()
.queue(task!(preload_load_event: move || {
let this = this.root();
this
.upcast::<EventTarget>()
.fire_event(atom!("load"), CanGc::note());
}));
}
}
}
impl StylesheetOwner for HTMLLinkElement {
@ -552,6 +781,21 @@ impl HTMLLinkElementMethods<crate::DomTypeHolder> for HTMLLinkElement {
.set_tokenlist_attribute(&local_name!("rel"), rel, can_gc);
}
// https://html.spec.whatwg.org/multipage/#dom-link-as
make_enumerated_getter!(
As,
"as",
"fetch" | "audio" | "audioworklet" | "document" | "embed" | "font" | "frame"
| "iframe" | "image" | "json" | "manifest" | "object" | "paintworklet"
| "report" | "script" | "serviceworker" | "sharedworker" | "style" | "track"
| "video" | "webidentity" | "worker" | "xslt",
missing => "",
invalid => ""
);
// https://html.spec.whatwg.org/multipage/#dom-link-as
make_setter!(SetAs, "as");
// https://html.spec.whatwg.org/multipage/#dom-link-media
make_getter!(Media, "media");
@ -689,6 +933,8 @@ impl LinkProcessingOptions {
self.has_trustworthy_ancestor_origin,
self.policy_container,
)
.initiator(Initiator::Link)
.origin(self.origin)
.integrity_metadata(self.integrity)
.cryptographic_nonce_metadata(self.cryptographic_nonce_metadata)
.referrer_policy(self.referrer_policy);
@ -795,3 +1041,77 @@ impl PreInvoke for PrefetchContext {
true
}
}
struct PreloadContext {
/// The `<link>` element that caused this preload operation
link: Trusted<HTMLLinkElement>,
resource_timing: ResourceFetchTiming,
/// The url being preloaded
url: ServoUrl,
}
impl FetchResponseListener for PreloadContext {
fn process_request_body(&mut self, _: RequestId) {}
fn process_request_eof(&mut self, _: RequestId) {}
fn process_response(
&mut self,
_: RequestId,
fetch_metadata: Result<FetchMetadata, NetworkError>,
) {
_ = fetch_metadata;
}
fn process_response_chunk(&mut self, _: RequestId, chunk: Vec<u8>) {
_ = chunk;
}
/// Step 3.1 of <https://html.spec.whatwg.org/multipage/#link-type-preload:fetch-and-process-the-linked-resource-2>
fn process_response_eof(
&mut self,
_: RequestId,
response: Result<ResourceFetchTiming, NetworkError>,
) {
self.link.root().fire_event_after_response(response);
}
fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming {
&mut self.resource_timing
}
fn resource_timing(&self) -> &ResourceFetchTiming {
&self.resource_timing
}
fn submit_resource_timing(&mut self) {
submit_timing(self, CanGc::note())
}
fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<csp::Violation>) {
let global = &self.resource_timing_global();
global.report_csp_violations(violations, None);
}
}
impl ResourceTimingListener for PreloadContext {
fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
(
InitiatorType::LocalName(self.url.clone().into_string()),
self.url.clone(),
)
}
fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
self.link.root().upcast::<Node>().owner_doc().global()
}
}
impl PreInvoke for PreloadContext {
fn should_invoke(&self) -> bool {
// Preload requests are never aborted.
true
}
}

View file

@ -13,7 +13,7 @@ interface HTMLLinkElement : HTMLElement {
attribute DOMString? crossOrigin;
[CEReactions]
attribute DOMString rel;
// [CEReactions] attribute DOMString as;
[CEReactions] attribute DOMString as;
[SameObject, PutForwards=value] readonly attribute DOMTokenList relList;
[CEReactions]
attribute DOMString media;

View file

@ -40,6 +40,7 @@ pub mod blob_url_store;
pub mod filemanager_thread;
pub mod http_status;
pub mod image_cache;
pub mod mime_classifier;
pub mod policy_container;
pub mod pub_domains;
pub mod quality;

View file

@ -3,7 +3,8 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use mime::{self, Mime};
use net_traits::LoadContext;
use crate::LoadContext;
pub struct MimeClassifier {
image_classifier: GroupedClassifier,
@ -16,11 +17,15 @@ pub struct MimeClassifier {
font_classifier: GroupedClassifier,
}
#[derive(PartialEq)]
pub enum MediaType {
Xml,
Html,
AudioVideo,
Image,
JavaScript,
Json,
Font,
}
pub enum ApacheBugFlag {
@ -99,7 +104,11 @@ impl MimeClassifier {
Some(MediaType::AudioVideo) => {
self.audio_video_classifier.classify(data)
},
Some(MediaType::Xml) | None => None,
Some(MediaType::JavaScript) |
Some(MediaType::Font) |
Some(MediaType::Json) |
Some(MediaType::Xml) |
None => None,
}
.unwrap_or(supplied_type.clone())
},
@ -215,20 +224,24 @@ impl MimeClassifier {
.expect("BinaryOrPlaintextClassifier always succeeds")
}
/// <https://mimesniff.spec.whatwg.org/#xml-mime-type>
fn is_xml(mt: &Mime) -> bool {
mt.suffix() == Some(mime::XML) ||
(mt.type_() == mime::APPLICATION && mt.subtype() == mime::XML) ||
(mt.type_() == mime::TEXT && mt.subtype() == mime::XML)
*mt == mime::TEXT_XML ||
(mt.type_() == mime::APPLICATION && mt.subtype() == mime::XML)
}
/// <https://mimesniff.spec.whatwg.org/#html-mime-type>
fn is_html(mt: &Mime) -> bool {
mt.type_() == mime::TEXT && mt.subtype() == mime::HTML
*mt == mime::TEXT_HTML
}
/// <https://mimesniff.spec.whatwg.org/#image-mime-type>
fn is_image(mt: &Mime) -> bool {
mt.type_() == mime::IMAGE
}
/// <https://mimesniff.spec.whatwg.org/#audio-or-video-mime-type>
fn is_audio_video(mt: &Mime) -> bool {
mt.type_() == mime::AUDIO ||
mt.type_() == mime::VIDEO ||
@ -241,7 +254,53 @@ impl MimeClassifier {
mt.type_() == mime::STAR && mt.subtype() == mime::STAR
}
fn get_media_type(mime: &Mime) -> Option<MediaType> {
/// <https://mimesniff.spec.whatwg.org/#javascript-mime-type>
fn is_javascript(mt: &Mime) -> bool {
(mt.type_() == mime::APPLICATION &&
(["ecmascript", "javascript", "x-ecmascript", "x-javascript"]
.contains(&mt.subtype().as_str()))) ||
(mt.type_() == mime::TEXT &&
([
"ecmascript",
"javascript",
"javascript1.0",
"javascript1.1",
"javascript1.2",
"javascript1.3",
"javascript1.4",
"javascript1.5",
"jscript",
"livescript",
"x-ecmascript",
"x-javascript",
]
.contains(&mt.subtype().as_str())))
}
/// <https://mimesniff.spec.whatwg.org/#json-mime-type>
fn is_json(mt: &Mime) -> bool {
mt.suffix() == Some(mime::JSON) ||
(mt.subtype() == mime::JSON &&
(mt.type_() == mime::APPLICATION || mt.type_() == mime::TEXT))
}
/// <https://mimesniff.spec.whatwg.org/#font-mime-type>
fn is_font(mt: &Mime) -> bool {
mt.type_() == mime::FONT ||
(mt.type_() == mime::APPLICATION &&
([
"font-cff",
"font-off",
"font-sfnt",
"font-ttf",
"font-woff",
"vnd.ms-fontobject",
"vnd.ms-opentype",
]
.contains(&mt.subtype().as_str())))
}
pub fn get_media_type(mime: &Mime) -> Option<MediaType> {
if MimeClassifier::is_xml(mime) {
Some(MediaType::Xml)
} else if MimeClassifier::is_html(mime) {
@ -250,6 +309,12 @@ impl MimeClassifier {
Some(MediaType::Image)
} else if MimeClassifier::is_audio_video(mime) {
Some(MediaType::AudioVideo)
} else if MimeClassifier::is_javascript(mime) {
Some(MediaType::JavaScript)
} else if MimeClassifier::is_font(mime) {
Some(MediaType::Font)
} else if MimeClassifier::is_json(mime) {
Some(MediaType::Json)
} else {
None
}

View file

@ -8,8 +8,8 @@ use std::io::{self, Read};
use std::path::{self, PathBuf};
use mime::{self, Mime};
use net::mime_classifier::{ApacheBugFlag, MimeClassifier, Mp4Matcher, NoSniffFlag};
use net_traits::LoadContext;
use net_traits::mime_classifier::{ApacheBugFlag, MimeClassifier, Mp4Matcher, NoSniffFlag};
fn read_file(path: &path::Path) -> io::Result<Vec<u8>> {
let mut file = File::open(path)?;

View file

@ -1,157 +1,157 @@
%PDF-1.2
%âãÏÓ
9 0 obj
<<
/Length 10 0 R
/Filter /FlateDecode
>>
stream
%PDF-1.2
%âãÏÓ
9 0 obj
<<
/Length 10 0 R
/Filter /FlateDecode
>>
stream
H‰Í<EFBFBD>ŃJĂ0†ź ďđ{§˛fç$M“ínŇ-Ť<C5A4>[&jeŠâŰۤ ń~$ÉÉ˙}ÉÉ…¬Ij«¬ĚsŔ—Ç~€XÖ-],÷‚$Y—÷Ó)ü'N«u­1!ś„ŔVŮ?źÁ?
žb1RbbœÒ‰ÉH²[¹™TD:#ž&Ø­ÙÌX®¦øiç»$qnf¬ƒ¿¶]»ÀõËîãaÿ¶{ÿÂØ£‰×q|JªLs]™QÒI¸¬jî„%¯Œ9Øé`ß঺¼ÅU»ite<74>zÛ$›’Ú¿OeBÆÄÒ¯á¸Råþ@zÜ—úóÿgª¼ø<õ¡ª
endstream
endobj
10 0 obj
246
endobj
4 0 obj
<<
/Type /Page
/Parent 5 0 R
/Resources <<
/Font <<
/F0 6 0 R
/F1 7 0 R
>>
/ProcSet 2 0 R
>>
/Contents 9 0 R
>>
endobj
6 0 obj
<<
/Type /Font
/Subtype /TrueType
/Name /F0
/BaseFont /Arial
/Encoding /WinAnsiEncoding
>>
endobj
7 0 obj
<<
/Type /Font
/Subtype /TrueType
/Name /F1
/BaseFont /BookAntiqua,Bold
/FirstChar 31
/LastChar 255
/Widths [ 750 250 278 402 606 500 889 833 227 333 333 444 606 250 333 250
296 500 500 500 500 500 500 500 500 500 500 250 250 606 606 606
444 747 778 667 722 833 611 556 833 833 389 389 778 611 1000 833
833 611 833 722 611 667 778 778 1000 667 667 667 333 606 333 606
500 333 500 611 444 611 500 389 556 611 333 333 611 333 889 611
556 611 611 389 444 333 611 556 833 500 556 500 310 606 310 606
750 500 750 333 500 500 1000 500 500 333 1000 611 389 1000 750 750
750 750 278 278 500 500 606 500 1000 333 998 444 389 833 750 750
667 250 278 500 500 606 500 606 500 333 747 438 500 606 333 747
500 400 549 361 361 333 576 641 250 333 361 488 500 889 890 889
444 778 778 778 778 778 778 1000 722 611 611 611 611 389 389 389
389 833 833 833 833 833 833 833 606 833 778 778 778 778 667 611
611 500 500 500 500 500 500 778 444 500 500 500 500 333 333 333
333 556 611 556 556 556 556 556 549 556 611 611 611 611 556 611
556 ]
/Encoding /WinAnsiEncoding
/FontDescriptor 8 0 R
>>
endobj
8 0 obj
<<
/Type /FontDescriptor
/FontName /BookAntiqua,Bold
/Flags 16418
/FontBBox [ -250 -260 1236 930 ]
/MissingWidth 750
/StemV 146
/StemH 146
/ItalicAngle 0
/CapHeight 930
/XHeight 651
/Ascent 930
/Descent 260
/Leading 210
/MaxWidth 1030
/AvgWidth 460
>>
endobj
2 0 obj
[ /PDF /Text ]
endobj
5 0 obj
<<
/Kids [4 0 R ]
/Count 1
/Type /Pages
/MediaBox [ 0 0 612 792 ]
>>
endobj
1 0 obj
<<
/Creator (1725.fm)
/CreationDate (1-Jan-3 18:15PM)
/Title (1725.PDF)
/Author (Unknown)
/Producer (Acrobat PDFWriter 3.02 for Windows)
/Keywords ()
/Subject ()
>>
endobj
3 0 obj
<<
/Pages 5 0 R
/Type /Catalog
/DefaultGray 11 0 R
/DefaultRGB 12 0 R
>>
endobj
11 0 obj
[/CalGray
<<
/WhitePoint [0.9505 1 1.0891 ]
/Gamma 0.2468
>>
]
endobj
12 0 obj
[/CalRGB
<<
/WhitePoint [0.9505 1 1.0891 ]
/Gamma [0.2468 0.2468 0.2468 ]
/Matrix [0.4361 0.2225 0.0139 0.3851 0.7169 0.0971 0.1431 0.0606 0.7141 ]
>>
]
endobj
xref
0 13
0000000000 65535 f
0000002172 00000 n
0000002046 00000 n
0000002363 00000 n
0000000375 00000 n
0000002080 00000 n
0000000518 00000 n
0000000633 00000 n
0000001760 00000 n
0000000021 00000 n
0000000352 00000 n
0000002460 00000 n
0000002548 00000 n
trailer
<<
/Size 13
/Root 3 0 R
/Info 1 0 R
/ID [<47149510433dd4882f05f8c124223734><47149510433dd4882f05f8c124223734>]
>>
startxref
2726
%%EOF
žb1RbbœÒ‰ÉH²[¹™TD:#ž&Ø­ÙÌX®¦øiç»$qnf¬ƒ¿¶]»ÀõËîãaÿ¶{ÿÂØ£‰×q|JªLs]™QÒI¸¬jî„%¯Œ9Øé`ß঺¼ÅU»ite<74>zÛ$›’Ú¿OeBÆÄÒ¯á¸Råþ@zÜ—úóÿgª¼ø<õ¡ª
endstream
endobj
10 0 obj
246
endobj
4 0 obj
<<
/Type /Page
/Parent 5 0 R
/Resources <<
/Font <<
/F0 6 0 R
/F1 7 0 R
>>
/ProcSet 2 0 R
>>
/Contents 9 0 R
>>
endobj
6 0 obj
<<
/Type /Font
/Subtype /TrueType
/Name /F0
/BaseFont /Arial
/Encoding /WinAnsiEncoding
>>
endobj
7 0 obj
<<
/Type /Font
/Subtype /TrueType
/Name /F1
/BaseFont /BookAntiqua,Bold
/FirstChar 31
/LastChar 255
/Widths [ 750 250 278 402 606 500 889 833 227 333 333 444 606 250 333 250
296 500 500 500 500 500 500 500 500 500 500 250 250 606 606 606
444 747 778 667 722 833 611 556 833 833 389 389 778 611 1000 833
833 611 833 722 611 667 778 778 1000 667 667 667 333 606 333 606
500 333 500 611 444 611 500 389 556 611 333 333 611 333 889 611
556 611 611 389 444 333 611 556 833 500 556 500 310 606 310 606
750 500 750 333 500 500 1000 500 500 333 1000 611 389 1000 750 750
750 750 278 278 500 500 606 500 1000 333 998 444 389 833 750 750
667 250 278 500 500 606 500 606 500 333 747 438 500 606 333 747
500 400 549 361 361 333 576 641 250 333 361 488 500 889 890 889
444 778 778 778 778 778 778 1000 722 611 611 611 611 389 389 389
389 833 833 833 833 833 833 833 606 833 778 778 778 778 667 611
611 500 500 500 500 500 500 778 444 500 500 500 500 333 333 333
333 556 611 556 556 556 556 556 549 556 611 611 611 611 556 611
556 ]
/Encoding /WinAnsiEncoding
/FontDescriptor 8 0 R
>>
endobj
8 0 obj
<<
/Type /FontDescriptor
/FontName /BookAntiqua,Bold
/Flags 16418
/FontBBox [ -250 -260 1236 930 ]
/MissingWidth 750
/StemV 146
/StemH 146
/ItalicAngle 0
/CapHeight 930
/XHeight 651
/Ascent 930
/Descent 260
/Leading 210
/MaxWidth 1030
/AvgWidth 460
>>
endobj
2 0 obj
[ /PDF /Text ]
endobj
5 0 obj
<<
/Kids [4 0 R ]
/Count 1
/Type /Pages
/MediaBox [ 0 0 612 792 ]
>>
endobj
1 0 obj
<<
/Creator (1725.fm)
/CreationDate (1-Jan-3 18:15PM)
/Title (1725.PDF)
/Author (Unknown)
/Producer (Acrobat PDFWriter 3.02 for Windows)
/Keywords ()
/Subject ()
>>
endobj
3 0 obj
<<
/Pages 5 0 R
/Type /Catalog
/DefaultGray 11 0 R
/DefaultRGB 12 0 R
>>
endobj
11 0 obj
[/CalGray
<<
/WhitePoint [0.9505 1 1.0891 ]
/Gamma 0.2468
>>
]
endobj
12 0 obj
[/CalRGB
<<
/WhitePoint [0.9505 1 1.0891 ]
/Gamma [0.2468 0.2468 0.2468 ]
/Matrix [0.4361 0.2225 0.0139 0.3851 0.7169 0.0971 0.1431 0.0606 0.7141 ]
>>
]
endobj
xref
0 13
0000000000 65535 f
0000002172 00000 n
0000002046 00000 n
0000002363 00000 n
0000000375 00000 n
0000002080 00000 n
0000000518 00000 n
0000000633 00000 n
0000001760 00000 n
0000000021 00000 n
0000000352 00000 n
0000002460 00000 n
0000002548 00000 n
trailer
<<
/Size 13
/Root 3 0 R
/Info 1 0 R
/ID [<47149510433dd4882f05f8c124223734><47149510433dd4882f05f8c124223734>]
>>
startxref
2726
%%EOF

View file

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 14 B

After

Width:  |  Height:  |  Size: 14 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Before After
Before After

Some files were not shown because too many files have changed in this diff Show more