mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
Auto merge of #21280 - paavininanda:Update_the_image_data, r=jdm
Reacting to environment changes <!-- Please describe your changes on the following line: --> --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [x] These changes fix a subset of #11416 <!-- Either: --> - [x] These changes do not require tests because they will be added when the complete responsive images is complete <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/21280) <!-- Reviewable:end -->
This commit is contained in:
commit
9a83ab6297
13 changed files with 308 additions and 1935 deletions
|
@ -392,6 +392,8 @@ pub struct Document {
|
|||
salvageable: Cell<bool>,
|
||||
/// Whether the unload event has already been fired.
|
||||
fired_unload: Cell<bool>,
|
||||
/// List of responsive images
|
||||
responsive_images: DomRefCell<Vec<Dom<HTMLImageElement>>>,
|
||||
}
|
||||
|
||||
#[derive(JSTraceable, MallocSizeOf)]
|
||||
|
@ -2221,6 +2223,23 @@ impl Document {
|
|||
let counter = self.throw_on_dynamic_markup_insertion_counter.get();
|
||||
self.throw_on_dynamic_markup_insertion_counter.set(counter - 1);
|
||||
}
|
||||
|
||||
pub fn react_to_environment_changes(&self) {
|
||||
for image in self.responsive_images.borrow().iter() {
|
||||
image.react_to_environment_changes();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_responsive_image(&self, img: &HTMLImageElement) {
|
||||
self.responsive_images.borrow_mut().push(Dom::from_ref(img));
|
||||
}
|
||||
|
||||
pub fn unregister_responsive_image(&self, img: &HTMLImageElement) {
|
||||
let index = self.responsive_images.borrow().iter().position(|x| **x == *img);
|
||||
if let Some(i) = index {
|
||||
self.responsive_images.borrow_mut().remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(MallocSizeOf, PartialEq)]
|
||||
|
@ -2465,7 +2484,8 @@ impl Document {
|
|||
throw_on_dynamic_markup_insertion_counter: Cell::new(0),
|
||||
page_showing: Cell::new(false),
|
||||
salvageable: Cell::new(true),
|
||||
fired_unload: Cell::new(false)
|
||||
fired_unload: Cell::new(false),
|
||||
responsive_images: Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ use dom::htmlmapelement::HTMLMapElement;
|
|||
use dom::htmlpictureelement::HTMLPictureElement;
|
||||
use dom::htmlsourceelement::HTMLSourceElement;
|
||||
use dom::mouseevent::MouseEvent;
|
||||
use dom::node::{Node, NodeDamage, document_from_node, window_from_node};
|
||||
use dom::node::{Node, NodeDamage, document_from_node, window_from_node, UnbindContext};
|
||||
use dom::progressevent::ProgressEvent;
|
||||
use dom::values::UNSIGNED_LONG_MAX;
|
||||
use dom::virtualmethods::VirtualMethods;
|
||||
|
@ -44,7 +44,7 @@ use html5ever::{LocalName, Prefix};
|
|||
use ipc_channel::ipc;
|
||||
use ipc_channel::router::ROUTER;
|
||||
use microtask::{Microtask, MicrotaskRunnable};
|
||||
use mime::{Mime, TopLevel};
|
||||
use mime::{Mime, TopLevel, SubLevel};
|
||||
use net_traits::{FetchResponseListener, FetchMetadata, NetworkError, FetchResponseMsg};
|
||||
use net_traits::image::base::{Image, ImageMetadata};
|
||||
use net_traits::image_cache::{CanRequestImages, ImageCache, ImageOrMetadataAvailable};
|
||||
|
@ -61,6 +61,7 @@ use std::char;
|
|||
use std::collections::HashSet;
|
||||
use std::default::Default;
|
||||
use std::i32;
|
||||
use std::mem;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use style::attr::{AttrValue, LengthOrPercentageOrAuto, parse_double, parse_length, parse_unsigned_integer};
|
||||
use style::context::QuirksMode;
|
||||
|
@ -160,6 +161,8 @@ struct ImageContext {
|
|||
status: Result<(), NetworkError>,
|
||||
/// The cache ID for this request.
|
||||
id: PendingImageId,
|
||||
/// Used to mark abort
|
||||
aborted: Cell<bool>,
|
||||
}
|
||||
|
||||
impl FetchResponseListener for ImageContext {
|
||||
|
@ -178,6 +181,20 @@ impl FetchResponseListener for ImageContext {
|
|||
}
|
||||
});
|
||||
|
||||
// Step 14.5 of https://html.spec.whatwg.org/multipage/#img-environment-changes
|
||||
if let Some(metadata) = metadata.as_ref() {
|
||||
if let Some(ref content_type) = metadata.content_type {
|
||||
match content_type.clone().into_inner().0 {
|
||||
Mime(TopLevel::Multipart, SubLevel::Ext(s), _) => {
|
||||
if s == "x-mixed-replace" {
|
||||
self.aborted.set(true);
|
||||
}
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let status_code = metadata.as_ref().and_then(|m| {
|
||||
m.status.as_ref().map(|&(code, _)| code)
|
||||
}).unwrap_or(0);
|
||||
|
@ -204,7 +221,11 @@ impl FetchResponseListener for ImageContext {
|
|||
}
|
||||
}
|
||||
|
||||
impl PreInvoke for ImageContext {}
|
||||
impl PreInvoke for ImageContext {
|
||||
fn should_invoke(&self) -> bool {
|
||||
!self.aborted.get()
|
||||
}
|
||||
}
|
||||
|
||||
impl HTMLImageElement {
|
||||
/// Update the current image with a valid URL.
|
||||
|
@ -279,6 +300,7 @@ impl HTMLImageElement {
|
|||
image_cache: window.image_cache(),
|
||||
status: Ok(()),
|
||||
id: id,
|
||||
aborted: Cell::new(false),
|
||||
}));
|
||||
|
||||
let (action_sender, action_receiver) = ipc::channel().unwrap();
|
||||
|
@ -380,6 +402,30 @@ impl HTMLImageElement {
|
|||
window.add_pending_reflow();
|
||||
}
|
||||
|
||||
fn process_image_response_for_environment_change(&self,
|
||||
image: ImageResponse,
|
||||
src: DOMString,
|
||||
generation: u32,
|
||||
selected_pixel_density: f64) {
|
||||
match image {
|
||||
ImageResponse::Loaded(image, url) | ImageResponse::PlaceholderLoaded(image, url) => {
|
||||
self.pending_request.borrow_mut().metadata = Some(ImageMetadata {
|
||||
height: image.height,
|
||||
width: image.width
|
||||
});
|
||||
self.pending_request.borrow_mut().final_url = Some(url);
|
||||
self.pending_request.borrow_mut().image = Some(image);
|
||||
self.finish_reacting_to_environment_change(src, generation, selected_pixel_density);
|
||||
},
|
||||
ImageResponse::MetadataLoaded(meta) => {
|
||||
self.pending_request.borrow_mut().metadata = Some(meta);
|
||||
},
|
||||
ImageResponse::None => {
|
||||
self.abort_request(State::Unavailable, ImageRequestPhase::Pending);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#abort-the-image-request>
|
||||
fn abort_request(&self, state: State, request: ImageRequestPhase) {
|
||||
let mut request = match request {
|
||||
|
@ -590,7 +636,7 @@ impl HTMLImageElement {
|
|||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#select-an-image-source>
|
||||
fn select_image_source(&self) -> Option<(DOMString, f32)> {
|
||||
fn select_image_source(&self) -> Option<(DOMString, f64)> {
|
||||
// Step 1, 3
|
||||
self.update_source_set();
|
||||
let source_set = &*self.source_set.borrow_mut();
|
||||
|
@ -643,7 +689,7 @@ impl HTMLImageElement {
|
|||
}
|
||||
}
|
||||
let selected_source = img_sources.remove(best_candidate.1).clone();
|
||||
Some((DOMString::from_string(selected_source.url), selected_source.descriptor.den.unwrap() as f32))
|
||||
Some((DOMString::from_string(selected_source.url), selected_source.descriptor.den.unwrap() as f64))
|
||||
}
|
||||
|
||||
fn init_image_request(&self,
|
||||
|
@ -659,12 +705,12 @@ impl HTMLImageElement {
|
|||
request.blocker = Some(LoadBlocker::new(&*document, LoadType::Image(url.clone())));
|
||||
}
|
||||
|
||||
/// Step 12 of html.spec.whatwg.org/multipage/#update-the-image-data
|
||||
/// Step 13-17 of html.spec.whatwg.org/multipage/#update-the-image-data
|
||||
fn prepare_image_request(&self, url: &ServoUrl, src: &DOMString) {
|
||||
match self.image_request.get() {
|
||||
ImageRequestPhase::Pending => {
|
||||
if let Some(pending_url) = self.pending_request.borrow().parsed_url.clone() {
|
||||
// Step 12.1
|
||||
// Step 13
|
||||
if pending_url == *url {
|
||||
return
|
||||
}
|
||||
|
@ -673,12 +719,12 @@ impl HTMLImageElement {
|
|||
ImageRequestPhase::Current => {
|
||||
let mut current_request = self.current_request.borrow_mut();
|
||||
let mut pending_request = self.pending_request.borrow_mut();
|
||||
// step 12.4, create a new "image_request"
|
||||
// step 16, create a new "image_request"
|
||||
match (current_request.parsed_url.clone(), current_request.state) {
|
||||
(Some(parsed_url), State::PartiallyAvailable) => {
|
||||
// Step 12.2
|
||||
// Step 14
|
||||
if parsed_url == *url {
|
||||
// 12.3 abort pending request
|
||||
// Step 15 abort pending request
|
||||
pending_request.image = None;
|
||||
pending_request.parsed_url = None;
|
||||
LoadBlocker::terminate(&mut pending_request.blocker);
|
||||
|
@ -689,11 +735,11 @@ impl HTMLImageElement {
|
|||
self.init_image_request(&mut pending_request, &url, &src);
|
||||
},
|
||||
(_, State::Broken) | (_, State::Unavailable) => {
|
||||
// Step 12.5
|
||||
// Step 17
|
||||
self.init_image_request(&mut current_request, &url, &src);
|
||||
},
|
||||
(_, _) => {
|
||||
// step 12.6
|
||||
// step 17
|
||||
self.image_request.set(ImageRequestPhase::Pending);
|
||||
self.init_image_request(&mut pending_request, &url, &src);
|
||||
},
|
||||
|
@ -711,7 +757,7 @@ impl HTMLImageElement {
|
|||
let this = Trusted::new(self);
|
||||
let src = match self.select_image_source() {
|
||||
Some(src) => {
|
||||
// Step 8.
|
||||
// Step 8
|
||||
// TODO: Handle pixel density.
|
||||
src.0
|
||||
},
|
||||
|
@ -727,7 +773,10 @@ impl HTMLImageElement {
|
|||
current_request.source_url = None;
|
||||
current_request.parsed_url = None;
|
||||
}
|
||||
if this.upcast::<Element>().has_attribute(&local_name!("src")) {
|
||||
let elem = this.upcast::<Element>();
|
||||
let src_present = elem.has_attribute(&local_name!("src"));
|
||||
|
||||
if src_present || Self::uses_srcset_or_picture(elem) {
|
||||
this.upcast::<EventTarget>().fire_event(atom!("error"));
|
||||
}
|
||||
// FIXME(nox): According to the spec, setting the current
|
||||
|
@ -766,11 +815,11 @@ impl HTMLImageElement {
|
|||
let parsed_url = base_url.join(&src);
|
||||
match parsed_url {
|
||||
Ok(url) => {
|
||||
// Step 12
|
||||
// Step 13-17
|
||||
self.prepare_image_request(&url, &src);
|
||||
},
|
||||
Err(_) => {
|
||||
// Step 11.1-11.5.
|
||||
// Step 12.1-12.5.
|
||||
let src = String::from(src);
|
||||
// FIXME(nox): Why are errors silenced here?
|
||||
let _ = task_source.queue(
|
||||
|
@ -882,6 +931,183 @@ impl HTMLImageElement {
|
|||
ScriptThread::await_stable_state(Microtask::ImageElement(task));
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#img-environment-changes>
|
||||
pub fn react_to_environment_changes(&self) {
|
||||
// Step 1
|
||||
let task = ImageElementMicrotask::EnvironmentChangesTask {
|
||||
elem: DomRoot::from_ref(self),
|
||||
generation: self.generation.get(),
|
||||
};
|
||||
ScriptThread::await_stable_state(Microtask::ImageElement(task));
|
||||
}
|
||||
|
||||
/// Step 2-12 of https://html.spec.whatwg.org/multipage/#img-environment-changes
|
||||
fn react_to_environment_changes_sync_steps(&self, generation: u32) {
|
||||
// TODO reduce duplicacy of this code
|
||||
fn add_cache_listener_for_element(image_cache: Arc<ImageCache>,
|
||||
id: PendingImageId,
|
||||
elem: &HTMLImageElement,
|
||||
selected_source: String,
|
||||
selected_pixel_density: f64) {
|
||||
let trusted_node = Trusted::new(elem);
|
||||
let (responder_sender, responder_receiver) = ipc::channel().unwrap();
|
||||
|
||||
let window = window_from_node(elem);
|
||||
let task_source = window.networking_task_source();
|
||||
let task_canceller = window.task_canceller(TaskSourceName::Networking);
|
||||
let generation = elem.generation.get();
|
||||
ROUTER.add_route(responder_receiver.to_opaque(), Box::new(move |message| {
|
||||
debug!("Got image {:?}", message);
|
||||
// Return the image via a message to the script thread, which marks
|
||||
// the element as dirty and triggers a reflow.
|
||||
let element = trusted_node.clone();
|
||||
let image = message.to().unwrap();
|
||||
let selected_source_clone = selected_source.clone();
|
||||
let _ = task_source.queue_with_canceller(
|
||||
task!(process_image_response_for_environment_change: move || {
|
||||
let element = element.root();
|
||||
// Ignore any image response for a previous request that has been discarded.
|
||||
if generation == element.generation.get() {
|
||||
element.process_image_response_for_environment_change(image,
|
||||
DOMString::from_string(selected_source_clone), generation, selected_pixel_density);
|
||||
}
|
||||
}),
|
||||
&task_canceller,
|
||||
);
|
||||
}));
|
||||
|
||||
image_cache.add_listener(id, ImageResponder::new(responder_sender, id));
|
||||
}
|
||||
|
||||
let elem = self.upcast::<Element>();
|
||||
let document = document_from_node(elem);
|
||||
let has_pending_request = match self.image_request.get() {
|
||||
ImageRequestPhase::Pending => true,
|
||||
_ => false
|
||||
};
|
||||
|
||||
// Step 2
|
||||
if !document.is_active() || !Self::uses_srcset_or_picture(elem) || has_pending_request {
|
||||
return;
|
||||
}
|
||||
|
||||
// Steps 3-4
|
||||
let (selected_source, selected_pixel_density) = match self.select_image_source() {
|
||||
Some(selected) => selected,
|
||||
None => return,
|
||||
};
|
||||
|
||||
// Step 5
|
||||
let same_source = match *self.last_selected_source.borrow() {
|
||||
Some(ref last_src) => *last_src == selected_source,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
let same_selected_pixel_density = match self.current_request.borrow().current_pixel_density {
|
||||
Some(den) => selected_pixel_density == den,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if same_source && same_selected_pixel_density {
|
||||
return;
|
||||
}
|
||||
|
||||
let base_url = document.base_url();
|
||||
// Step 6
|
||||
let img_url = match base_url.join(&selected_source) {
|
||||
Ok(url) => url,
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
// Step 12
|
||||
self.image_request.set(ImageRequestPhase::Pending);
|
||||
self.init_image_request(&mut self.pending_request.borrow_mut(), &img_url, &selected_source);
|
||||
|
||||
let window = window_from_node(self);
|
||||
let image_cache = window.image_cache();
|
||||
// Step 14
|
||||
let response =
|
||||
image_cache.find_image_or_metadata(img_url.clone().into(),
|
||||
UsePlaceholder::No,
|
||||
CanRequestImages::Yes);
|
||||
match response {
|
||||
Ok(ImageOrMetadataAvailable::ImageAvailable(_image, _url)) => {
|
||||
// Step 15
|
||||
self.finish_reacting_to_environment_change(selected_source, generation, selected_pixel_density);
|
||||
}
|
||||
|
||||
Ok(ImageOrMetadataAvailable::MetadataAvailable(m)) => {
|
||||
self.process_image_response_for_environment_change(ImageResponse::MetadataLoaded(m),
|
||||
selected_source, generation, selected_pixel_density);
|
||||
}
|
||||
|
||||
Err(ImageState::Pending(id)) => {
|
||||
add_cache_listener_for_element(image_cache.clone(), id, self,
|
||||
selected_source.to_string(), selected_pixel_density);
|
||||
}
|
||||
|
||||
Err(ImageState::LoadError) => {
|
||||
self.process_image_response_for_environment_change(ImageResponse::None,
|
||||
selected_source, generation, selected_pixel_density);
|
||||
}
|
||||
|
||||
Err(ImageState::NotRequested(id)) => {
|
||||
add_cache_listener_for_element(image_cache, id, self, selected_source.to_string(),
|
||||
selected_pixel_density);
|
||||
self.fetch_request(&img_url, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Step 15 for <https://html.spec.whatwg.org/multipage/#img-environment-changes>
|
||||
fn finish_reacting_to_environment_change(&self, src: DOMString, generation: u32, selected_pixel_density: f64) {
|
||||
let this = Trusted::new(self);
|
||||
let window = window_from_node(self);
|
||||
let src = src.to_string();
|
||||
let _ = window.dom_manipulation_task_source().queue(
|
||||
task!(image_load_event: move || {
|
||||
let this = this.root();
|
||||
let relevant_mutation = this.generation.get() != generation;
|
||||
// Step 15.1
|
||||
if relevant_mutation {
|
||||
this.abort_request(State::Unavailable, ImageRequestPhase::Pending);
|
||||
return;
|
||||
}
|
||||
// Step 15.2
|
||||
*this.last_selected_source.borrow_mut() = Some(DOMString::from_string(src));
|
||||
|
||||
{
|
||||
let mut pending_request = this.pending_request.borrow_mut();
|
||||
pending_request.current_pixel_density = Some(selected_pixel_density);
|
||||
|
||||
// Step 15.3
|
||||
pending_request.state = State::CompletelyAvailable;
|
||||
|
||||
// Step 15.4
|
||||
// Already a part of the list of available images due to Step 14
|
||||
|
||||
// Step 15.5
|
||||
mem::swap(&mut this.current_request.borrow_mut(), &mut pending_request);
|
||||
this.abort_request(State::Unavailable, ImageRequestPhase::Pending);
|
||||
}
|
||||
|
||||
// Step 15.6
|
||||
this.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
|
||||
|
||||
// Step 15.7
|
||||
this.upcast::<EventTarget>().fire_event(atom!("load"));
|
||||
}),
|
||||
window.upcast(),
|
||||
);
|
||||
}
|
||||
|
||||
fn uses_srcset_or_picture(elem: &Element) -> bool {
|
||||
let has_src = elem.has_attribute(&local_name!("srcset"));
|
||||
let is_parent_picture = elem.upcast::<Node>().GetParentElement()
|
||||
.map_or(false, |p| p.is::<HTMLPictureElement>());
|
||||
has_src || is_parent_picture
|
||||
}
|
||||
|
||||
fn new_inherited(local_name: LocalName, prefix: Option<Prefix>, document: &Document) -> HTMLImageElement {
|
||||
HTMLImageElement {
|
||||
htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
|
||||
|
@ -974,6 +1200,10 @@ pub enum ImageElementMicrotask {
|
|||
StableStateUpdateImageDataTask {
|
||||
elem: DomRoot<HTMLImageElement>,
|
||||
generation: u32,
|
||||
},
|
||||
EnvironmentChangesTask {
|
||||
elem: DomRoot<HTMLImageElement>,
|
||||
generation: u32,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -987,6 +1217,9 @@ impl MicrotaskRunnable for ImageElementMicrotask {
|
|||
elem.update_the_image_data_sync_steps();
|
||||
}
|
||||
},
|
||||
&ImageElementMicrotask::EnvironmentChangesTask { ref elem, ref generation } => {
|
||||
elem.react_to_environment_changes_sync_steps(*generation);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1156,11 +1389,12 @@ impl HTMLImageElementMethods for HTMLImageElement {
|
|||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-img-currentsrc
|
||||
fn CurrentSrc(&self) -> DOMString {
|
||||
let ref url = self.current_request.borrow().parsed_url;
|
||||
let current_request = self.current_request.borrow();
|
||||
let ref url = current_request.parsed_url;
|
||||
match *url {
|
||||
Some(ref url) => DOMString::from_string(url.clone().into_string()),
|
||||
None => {
|
||||
let ref unparsed_url = self.current_request.borrow().source_url;
|
||||
let ref unparsed_url = current_request.source_url;
|
||||
match *unparsed_url {
|
||||
Some(ref url) => url.clone(),
|
||||
None => DOMString::from("")
|
||||
|
@ -1220,8 +1454,14 @@ impl VirtualMethods for HTMLImageElement {
|
|||
self.super_type().unwrap().attribute_mutated(attr, mutation);
|
||||
match attr.local_name() {
|
||||
&local_name!("src") => self.update_the_image_data(),
|
||||
&local_name!("srcset") => self.update_the_image_data(),
|
||||
_ => {},
|
||||
}
|
||||
if let Some(parent) = self.upcast::<Node>().GetParentElement() {
|
||||
if parent.is::<HTMLPictureElement>() {
|
||||
self.update_the_image_data();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
|
||||
|
@ -1268,6 +1508,27 @@ impl VirtualMethods for HTMLImageElement {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn bind_to_tree(&self, tree_in_doc: bool) {
|
||||
if let Some(ref s) = self.super_type() {
|
||||
s.bind_to_tree(tree_in_doc);
|
||||
}
|
||||
let document = document_from_node(self);
|
||||
if tree_in_doc {
|
||||
document.register_responsive_image(self);
|
||||
}
|
||||
if let Some(parent) = self.upcast::<Node>().GetParentElement() {
|
||||
if parent.is::<HTMLPictureElement>() {
|
||||
self.update_the_image_data();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn unbind_from_tree(&self, context: &UnbindContext) {
|
||||
self.super_type().unwrap().unbind_from_tree(context);
|
||||
let document = document_from_node(self);
|
||||
document.unregister_responsive_image(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl FormControl for HTMLImageElement {
|
||||
|
|
|
@ -1861,6 +1861,7 @@ impl Window {
|
|||
mql.Matches());
|
||||
event.upcast::<Event>().fire(mql.upcast::<EventTarget>());
|
||||
}
|
||||
self.Document().react_to_environment_changes();
|
||||
}
|
||||
|
||||
/// Slow down/speed up timers based on visibility.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue