aboutsummaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
Diffstat (limited to 'components')
-rw-r--r--components/bluetooth/lib.rs2
-rw-r--r--components/config/Cargo.toml2
-rw-r--r--components/devtools/protocol.rs5
-rw-r--r--components/layout_2020/display_list.rs145
-rw-r--r--components/net/http_cache.rs11
-rw-r--r--components/net/http_loader.rs3
-rw-r--r--components/net/resource_thread.rs17
-rw-r--r--components/net_traits/lib.rs5
-rw-r--r--components/profile/heartbeats.rs5
-rw-r--r--components/profile/time.rs7
-rw-r--r--components/script/dom/audiotrack.rs15
-rw-r--r--components/script/dom/audiotracklist.rs5
-rw-r--r--components/script/dom/bindings/interface.rs85
-rw-r--r--components/script/dom/bindings/trace.rs9
-rw-r--r--components/script/dom/create.rs19
-rw-r--r--components/script/dom/customelementregistry.rs48
-rw-r--r--components/script/dom/dedicatedworkerglobalscope.rs3
-rw-r--r--components/script/dom/document.rs60
-rw-r--r--components/script/dom/domimplementation.rs8
-rw-r--r--components/script/dom/globalscope.rs30
-rw-r--r--components/script/dom/headers.rs130
-rwxr-xr-xcomponents/script/dom/htmlbuttonelement.rs10
-rw-r--r--components/script/dom/htmlelement.rs70
-rwxr-xr-xcomponents/script/dom/htmlinputelement.rs41
-rw-r--r--components/script/dom/htmllabelelement.rs50
-rw-r--r--components/script/dom/htmlmediaelement.rs1
-rw-r--r--components/script/dom/htmlmeterelement.rs8
-rw-r--r--components/script/dom/htmloptionelement.rs38
-rw-r--r--components/script/dom/htmloutputelement.rs6
-rw-r--r--components/script/dom/htmlprogresselement.rs8
-rw-r--r--components/script/dom/htmlscriptelement.rs270
-rwxr-xr-xcomponents/script/dom/htmlselectelement.rs6
-rwxr-xr-xcomponents/script/dom/htmltextareaelement.rs6
-rw-r--r--components/script/dom/htmltrackelement.rs1
-rw-r--r--components/script/dom/macros.rs15
-rw-r--r--components/script/dom/nodelist.rs38
-rw-r--r--components/script/dom/promise.rs2
-rw-r--r--components/script/dom/texttrack.rs20
-rw-r--r--components/script/dom/texttracklist.rs4
-rw-r--r--components/script/dom/videotrack.rs15
-rw-r--r--components/script/dom/videotracklist.rs5
-rw-r--r--components/script/dom/webgl_validations/tex_image_2d.rs21
-rw-r--r--components/script/dom/webidls/CustomElementRegistry.webidl2
-rw-r--r--components/script/dom/webidls/Document.webidl5
-rw-r--r--components/script/dom/webidls/HTMLInputElement.webidl2
-rw-r--r--components/script/dom/webidls/HTMLOptionElement.webidl4
-rw-r--r--components/script/dom/webidls/HTMLScriptElement.webidl2
-rw-r--r--components/script/dom/worker.rs15
-rw-r--r--components/script/dom/xr.rs16
-rw-r--r--components/script/dom/xrsession.rs37
-rw-r--r--components/script/lib.rs2
-rw-r--r--components/script/script_module.rs1430
-rw-r--r--components/script/script_runtime.rs20
-rw-r--r--components/script_plugins/lib.rs4
-rw-r--r--components/style/properties/longhands/border.mako.rs2
-rw-r--r--components/style/properties/shorthands/border.mako.rs2
56 files changed, 2287 insertions, 505 deletions
diff --git a/components/bluetooth/lib.rs b/components/bluetooth/lib.rs
index 19d6db743e4..5c54860d9e3 100644
--- a/components/bluetooth/lib.rs
+++ b/components/bluetooth/lib.rs
@@ -288,7 +288,7 @@ impl BluetoothManager {
self.adapter = BluetoothAdapter::init_mock().ok();
match test::test(self, data_set_name) {
Ok(_) => return Ok(()),
- Err(error) => Err(BluetoothError::Type(error.description().to_owned())),
+ Err(error) => Err(BluetoothError::Type(error.to_string())),
}
}
diff --git a/components/config/Cargo.toml b/components/config/Cargo.toml
index 774c1b0f7bf..f930b60a5d3 100644
--- a/components/config/Cargo.toml
+++ b/components/config/Cargo.toml
@@ -30,5 +30,5 @@ servo_config_plugins = { path = "../config_plugins" }
[dev-dependencies]
std_test_override = { path = "../std_test_override" }
-[target.'cfg(not(any(target_os = "android", feature = "uwp")))'.dependencies]
+[target.'cfg(not(target_os = "android"))'.dependencies]
dirs = "1.0"
diff --git a/components/devtools/protocol.rs b/components/devtools/protocol.rs
index bfc547f6b75..29fbaa2beeb 100644
--- a/components/devtools/protocol.rs
+++ b/components/devtools/protocol.rs
@@ -8,7 +8,6 @@
use serde::Serialize;
use serde_json::{self, Value};
-use std::error::Error;
use std::io::{Read, Write};
use std::net::TcpStream;
@@ -62,7 +61,7 @@ impl JsonPacketStream for TcpStream {
Ok(0) => return Ok(None), // EOF
Ok(1) => buf[0],
Ok(_) => unreachable!(),
- Err(e) => return Err(e.description().to_owned()),
+ Err(e) => return Err(e.to_string()),
};
match byte {
b':' => {
@@ -79,7 +78,7 @@ impl JsonPacketStream for TcpStream {
debug!("{}", packet);
return match serde_json::from_str(&packet) {
Ok(json) => Ok(Some(json)),
- Err(err) => Err(err.description().to_owned()),
+ Err(err) => Err(err.to_string()),
};
},
c => buffer.push(c),
diff --git a/components/layout_2020/display_list.rs b/components/layout_2020/display_list.rs
index f39a2f23b18..6bd6c60a663 100644
--- a/components/layout_2020/display_list.rs
+++ b/components/layout_2020/display_list.rs
@@ -5,15 +5,21 @@
use crate::fragments::{BoxFragment, Fragment};
use crate::geom::physical::{Rect, Vec2};
use embedder_traits::Cursor;
-use euclid::{Point2D, SideOffsets2D};
+use euclid::{Point2D, SideOffsets2D, Size2D};
use gfx::text::glyph::GlyphStore;
use std::sync::Arc;
+use style::dom::OpaqueNode;
use style::properties::ComputedValues;
-use style::values::computed::{BorderStyle, Length};
-use webrender_api::{self as wr, units, CommonItemProperties, PrimitiveFlags};
+use style::values::computed::{BorderStyle, Length, LengthPercentage};
+use style::values::specified::ui::CursorKind;
+use webrender_api::{self as wr, units};
+
+// `webrender_api::display_item::ItemTag` is private
+type ItemTag = (u64, u16);
+type HitInfo = Option<ItemTag>;
pub struct DisplayListBuilder {
- pipeline_id: wr::PipelineId,
+ current_space_and_clip: wr::SpaceAndClipInfo,
pub wr: wr::DisplayListBuilder,
pub is_contentful: bool,
}
@@ -21,11 +27,33 @@ pub struct DisplayListBuilder {
impl DisplayListBuilder {
pub fn new(pipeline_id: wr::PipelineId, viewport_size: wr::units::LayoutSize) -> Self {
Self {
- pipeline_id,
+ current_space_and_clip: wr::SpaceAndClipInfo::root_scroll(pipeline_id),
is_contentful: false,
wr: wr::DisplayListBuilder::new(pipeline_id, viewport_size),
}
}
+
+ fn common_properties(
+ &self,
+ clip_rect: units::LayoutRect,
+ hit_info: HitInfo,
+ ) -> wr::CommonItemProperties {
+ wr::CommonItemProperties {
+ clip_rect,
+ clip_id: self.current_space_and_clip.clip_id,
+ spatial_id: self.current_space_and_clip.spatial_id,
+ hit_info,
+ // TODO(gw): Make use of the WR backface visibility functionality.
+ flags: wr::PrimitiveFlags::default(),
+ }
+ }
+
+ fn clipping_and_scrolling_scope<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
+ let previous = self.current_space_and_clip;
+ let result = f(self);
+ self.current_space_and_clip = previous;
+ result
+ }
}
/// Contentful paint, for the purpose of
@@ -60,19 +88,12 @@ impl Fragment {
.translate(&containing_block.top_left);
let mut baseline_origin = rect.top_left.clone();
baseline_origin.y += t.ascent;
- let cursor = cursor(&t.parent_style, Cursor::Text);
- let common = CommonItemProperties {
- clip_rect: rect.clone().into(),
- clip_id: wr::ClipId::root(builder.pipeline_id),
- spatial_id: wr::SpatialId::root_scroll_node(builder.pipeline_id),
- hit_info: cursor.map(|cursor| (t.tag.0 as u64, cursor as u16)),
- // TODO(gw): Make use of the WR backface visibility functionality.
- flags: PrimitiveFlags::default(),
- };
let glyphs = glyphs(&t.glyphs, baseline_origin);
if glyphs.is_empty() {
return;
}
+ let hit_info = hit_info(&t.parent_style, t.tag, Cursor::Text);
+ let common = builder.common_properties(rect.clone().into(), hit_info);
let color = t.parent_style.clone_color();
builder
.wr
@@ -85,14 +106,8 @@ impl Fragment {
.rect
.to_physical(i.style.writing_mode, containing_block)
.translate(&containing_block.top_left);
- let common = CommonItemProperties {
- clip_rect: rect.clone().into(),
- clip_id: wr::ClipId::root(builder.pipeline_id),
- spatial_id: wr::SpatialId::root_scroll_node(builder.pipeline_id),
- hit_info: None,
- // TODO(gw): Make use of the WR backface visibility functionality.
- flags: PrimitiveFlags::default(),
- };
+ let hit_info = None;
+ let common = builder.common_properties(rect.clone().into(), hit_info);
builder.wr.push_image(
&common,
rect.into(),
@@ -122,18 +137,11 @@ impl BoxFragment {
.to_physical(self.style.writing_mode, containing_block)
.translate(&containing_block.top_left)
.into();
- let cursor = cursor(&self.style, Cursor::Default);
- let common = CommonItemProperties {
- clip_rect: border_rect,
- clip_id: wr::ClipId::root(builder.pipeline_id),
- spatial_id: wr::SpatialId::root_scroll_node(builder.pipeline_id),
- hit_info: cursor.map(|cursor| (self.tag.0 as u64, cursor as u16)),
- // TODO(gw): Make use of the WR backface visibility functionality.
- flags: PrimitiveFlags::default(),
- };
+ let hit_info = hit_info(&self.style, self.tag, Cursor::Default);
+ let border_radius = self.border_radius(&border_rect);
- self.background_display_items(builder, &common);
- self.border_display_items(builder, &common, border_rect);
+ self.background_display_items(builder, hit_info, border_rect, &border_radius);
+ self.border_display_items(builder, hit_info, border_rect, border_radius);
let content_rect = self
.content_rect
.to_physical(self.style.writing_mode, containing_block)
@@ -143,24 +151,61 @@ impl BoxFragment {
}
}
+ fn border_radius(&self, border_rect: &units::LayoutRect) -> wr::BorderRadius {
+ let resolve = |radius: &LengthPercentage, box_size: f32| {
+ radius.percentage_relative_to(Length::new(box_size)).px()
+ };
+ let corner = |corner: &style::values::computed::BorderCornerRadius| {
+ Size2D::new(
+ resolve(&corner.0.width.0, border_rect.size.width),
+ resolve(&corner.0.height.0, border_rect.size.height),
+ )
+ };
+ let b = self.style.get_border();
+ wr::BorderRadius {
+ top_left: corner(&b.border_top_left_radius),
+ top_right: corner(&b.border_top_right_radius),
+ bottom_right: corner(&b.border_bottom_right_radius),
+ bottom_left: corner(&b.border_bottom_left_radius),
+ }
+ }
+
fn background_display_items(
&self,
builder: &mut DisplayListBuilder,
- common: &CommonItemProperties,
+ hit_info: HitInfo,
+ border_rect: units::LayoutRect,
+ border_radius: &wr::BorderRadius,
) {
let background_color = self
.style
.resolve_color(self.style.clone_background_color());
- if background_color.alpha > 0 || common.hit_info.is_some() {
- builder.wr.push_rect(common, rgba(background_color))
+ if background_color.alpha > 0 || hit_info.is_some() {
+ builder.clipping_and_scrolling_scope(|builder| {
+ if !border_radius.is_zero() {
+ builder.current_space_and_clip.clip_id = builder.wr.define_clip(
+ &builder.current_space_and_clip,
+ border_rect,
+ Some(wr::ComplexClipRegion {
+ rect: border_rect,
+ radii: *border_radius,
+ mode: wr::ClipMode::Clip,
+ }),
+ None,
+ );
+ }
+ let common = builder.common_properties(border_rect, hit_info);
+ builder.wr.push_rect(&common, rgba(background_color))
+ });
}
}
fn border_display_items(
&self,
builder: &mut DisplayListBuilder,
- common: &CommonItemProperties,
+ hit_info: HitInfo,
border_rect: units::LayoutRect,
+ radius: wr::BorderRadius,
) {
let b = self.style.get_border();
let widths = SideOffsets2D::new(
@@ -187,15 +232,18 @@ impl BoxFragment {
BorderStyle::Outset => wr::BorderStyle::Outset,
},
};
+ let common = builder.common_properties(border_rect, hit_info);
let details = wr::BorderDetails::Normal(wr::NormalBorder {
top: side(b.border_top_style, b.border_top_color),
right: side(b.border_right_style, b.border_right_color),
bottom: side(b.border_bottom_style, b.border_bottom_color),
left: side(b.border_left_style, b.border_left_color),
- radius: wr::BorderRadius::zero(),
+ radius,
do_aa: true,
});
- builder.wr.push_border(common, border_rect, widths, details)
+ builder
+ .wr
+ .push_border(&common, border_rect, widths, details)
}
}
@@ -233,16 +281,21 @@ fn glyphs(glyph_runs: &[Arc<GlyphStore>], mut origin: Vec2<Length>) -> Vec<wr::G
glyphs
}
-fn cursor(values: &ComputedValues, default: Cursor) -> Option<Cursor> {
+fn hit_info(style: &ComputedValues, tag: OpaqueNode, auto_cursor: Cursor) -> HitInfo {
use style::computed_values::pointer_events::T as PointerEvents;
- use style::values::specified::ui::CursorKind;
- let inherited_ui = values.get_inherited_ui();
+ let inherited_ui = style.get_inherited_ui();
if inherited_ui.pointer_events == PointerEvents::None {
- return None;
+ None
+ } else {
+ let cursor = cursor(inherited_ui.cursor.keyword, auto_cursor);
+ Some((tag.0 as u64, cursor as u16))
}
- Some(match inherited_ui.cursor.keyword {
- CursorKind::Auto => default,
+}
+
+fn cursor(kind: CursorKind, auto_cursor: Cursor) -> Cursor {
+ match kind {
+ CursorKind::Auto => auto_cursor,
CursorKind::None => Cursor::None,
CursorKind::Default => Cursor::Default,
CursorKind::Pointer => Cursor::Pointer,
@@ -278,5 +331,5 @@ fn cursor(values: &ComputedValues, default: Cursor) -> Option<Cursor> {
CursorKind::AllScroll => Cursor::AllScroll,
CursorKind::ZoomIn => Cursor::ZoomIn,
CursorKind::ZoomOut => Cursor::ZoomOut,
- })
+ }
}
diff --git a/components/net/http_cache.rs b/components/net/http_cache.rs
index e47b4e81951..592ad7a99aa 100644
--- a/components/net/http_cache.rs
+++ b/components/net/http_cache.rs
@@ -230,12 +230,15 @@ fn get_response_expiry(response: &Response) -> Duration {
let max_heuristic = Duration::hours(24) - age;
let heuristic_freshness = if let Some(last_modified) =
// If the response has a Last-Modified header field,
- // caches are encouraged to use a heuristic expiration value
- // that is no more than some fraction of the interval since that time.
- response.headers.typed_get::<LastModified>() {
+ // caches are encouraged to use a heuristic expiration value
+ // that is no more than some fraction of the interval since that time.
+ response.headers.typed_get::<LastModified>()
+ {
let current = time::now().to_timespec();
let last_modified: SystemTime = last_modified.into();
- let last_modified = last_modified.duration_since(SystemTime::UNIX_EPOCH).unwrap();
+ let last_modified = last_modified
+ .duration_since(SystemTime::UNIX_EPOCH)
+ .unwrap();
let last_modified = Timespec::new(last_modified.as_secs() as i64, 0);
// A typical setting of this fraction might be 10%.
let raw_heuristic_calc = (current - last_modified) / 10;
diff --git a/components/net/http_loader.rs b/components/net/http_loader.rs
index 18e4e696f80..7868556adfe 100644
--- a/components/net/http_loader.rs
+++ b/components/net/http_loader.rs
@@ -44,7 +44,6 @@ use net_traits::{
use servo_arc::Arc;
use servo_url::{ImmutableOrigin, ServoUrl};
use std::collections::{HashMap, HashSet};
-use std::error::Error;
use std::iter::FromIterator;
use std::mem;
use std::ops::Deref;
@@ -622,7 +621,7 @@ pub fn http_fetch(
HeaderValue::to_str(v)
.map(|l| {
ServoUrl::parse_with_base(response.actual_response().url(), &l)
- .map_err(|err| err.description().into())
+ .map_err(|err| err.to_string())
})
.ok()
});
diff --git a/components/net/resource_thread.rs b/components/net/resource_thread.rs
index c948055bc55..f85d623fe4f 100644
--- a/components/net/resource_thread.rs
+++ b/components/net/resource_thread.rs
@@ -40,7 +40,6 @@ use servo_arc::Arc as ServoArc;
use servo_url::ServoUrl;
use std::borrow::{Cow, ToOwned};
use std::collections::HashMap;
-use std::error::Error;
use std::fs::{self, File};
use std::io::prelude::*;
use std::ops::Deref;
@@ -361,7 +360,7 @@ where
let mut file = match File::open(&path) {
Err(why) => {
- warn!("couldn't open {}: {}", display, Error::description(&why));
+ warn!("couldn't open {}: {}", display, why);
return;
},
Ok(file) => file,
@@ -369,11 +368,7 @@ where
let mut string_buffer: String = String::new();
match file.read_to_string(&mut string_buffer) {
- Err(why) => panic!(
- "couldn't read from {}: {}",
- display,
- Error::description(&why)
- ),
+ Err(why) => panic!("couldn't read from {}: {}", display, why),
Ok(_) => println!("successfully read from {}", display),
}
@@ -396,16 +391,12 @@ where
let display = path.display();
let mut file = match File::create(&path) {
- Err(why) => panic!("couldn't create {}: {}", display, Error::description(&why)),
+ Err(why) => panic!("couldn't create {}: {}", display, why),
Ok(file) => file,
};
match file.write_all(json_encoded.as_bytes()) {
- Err(why) => panic!(
- "couldn't write to {}: {}",
- display,
- Error::description(&why)
- ),
+ Err(why) => panic!("couldn't write to {}: {}", display, why),
Ok(_) => println!("successfully wrote to {}", display),
}
}
diff --git a/components/net_traits/lib.rs b/components/net_traits/lib.rs
index a25a804ce01..f7b291e420f 100644
--- a/components/net_traits/lib.rs
+++ b/components/net_traits/lib.rs
@@ -31,7 +31,6 @@ use ipc_channel::Error as IpcError;
use mime::Mime;
use msg::constellation_msg::HistoryStateId;
use servo_url::ServoUrl;
-use std::error::Error;
use time::precise_time_ns;
use webrender_api::ImageKey;
@@ -699,11 +698,11 @@ pub enum NetworkError {
impl NetworkError {
pub fn from_hyper_error(error: &HyperError) -> Self {
- NetworkError::Internal(error.description().to_owned())
+ NetworkError::Internal(error.to_string())
}
pub fn from_http_error(error: &HttpError) -> Self {
- NetworkError::Internal(error.description().to_owned())
+ NetworkError::Internal(error.to_string())
}
}
diff --git a/components/profile/heartbeats.rs b/components/profile/heartbeats.rs
index ac29d12bbb3..5a09ea4f9e6 100644
--- a/components/profile/heartbeats.rs
+++ b/components/profile/heartbeats.rs
@@ -7,7 +7,6 @@ use heartbeats_simple::HeartbeatPow as Heartbeat;
use profile_traits::time::ProfilerCategory;
use std::collections::HashMap;
use std::env::var_os;
-use std::error::Error;
use std::fs::File;
use std::path::Path;
@@ -85,7 +84,7 @@ fn open_heartbeat_log<P: AsRef<Path>>(name: P) -> Option<File> {
match File::create(name) {
Ok(f) => Some(f),
Err(e) => {
- warn!("Failed to open heartbeat log: {}", Error::description(&e));
+ warn!("Failed to open heartbeat log: {}", e);
None
},
}
@@ -138,7 +137,7 @@ fn maybe_create_heartbeat(
fn log_heartbeat_records(hb: &mut Heartbeat) {
match hb.log_to_buffer_index() {
Ok(_) => (),
- Err(e) => warn!("Failed to write heartbeat log: {}", Error::description(&e)),
+ Err(e) => warn!("Failed to write heartbeat log: {}", e),
}
}
diff --git a/components/profile/time.rs b/components/profile/time.rs
index 3dac5663799..cea75b54b44 100644
--- a/components/profile/time.rs
+++ b/components/profile/time.rs
@@ -19,7 +19,6 @@ use servo_config::opts::OutputOptions;
use std::borrow::ToOwned;
use std::cmp::Ordering;
use std::collections::{BTreeMap, HashMap};
-use std::error::Error;
use std::fs::File;
use std::io::{self, Write};
use std::path::Path;
@@ -397,11 +396,7 @@ impl Profiler {
Some(OutputOptions::FileName(ref filename)) => {
let path = Path::new(&filename);
let mut file = match File::create(&path) {
- Err(e) => panic!(
- "Couldn't create {}: {}",
- path.display(),
- Error::description(&e)
- ),
+ Err(e) => panic!("Couldn't create {}: {}", path.display(), e),
Ok(file) => file,
};
write!(
diff --git a/components/script/dom/audiotrack.rs b/components/script/dom/audiotrack.rs
index b66ea5bf4cc..af237bc8b1a 100644
--- a/components/script/dom/audiotrack.rs
+++ b/components/script/dom/audiotrack.rs
@@ -3,6 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use crate::dom::audiotracklist::AudioTrackList;
+use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::AudioTrackBinding::{self, AudioTrackMethods};
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot};
@@ -19,7 +20,7 @@ pub struct AudioTrack {
label: DOMString,
language: DOMString,
enabled: Cell<bool>,
- track_list: Option<Dom<AudioTrackList>>,
+ track_list: DomRefCell<Option<Dom<AudioTrackList>>>,
}
impl AudioTrack {
@@ -37,7 +38,7 @@ impl AudioTrack {
label: label.into(),
language: language.into(),
enabled: Cell::new(false),
- track_list: track_list.map(|t| Dom::from_ref(t)),
+ track_list: DomRefCell::new(track_list.map(|t| Dom::from_ref(t))),
}
}
@@ -73,6 +74,14 @@ impl AudioTrack {
pub fn set_enabled(&self, value: bool) {
self.enabled.set(value);
}
+
+ pub fn add_track_list(&self, track_list: &AudioTrackList) {
+ *self.track_list.borrow_mut() = Some(Dom::from_ref(track_list));
+ }
+
+ pub fn remove_track_list(&self) {
+ *self.track_list.borrow_mut() = None;
+ }
}
impl AudioTrackMethods for AudioTrack {
@@ -103,7 +112,7 @@ impl AudioTrackMethods for AudioTrack {
// https://html.spec.whatwg.org/multipage/#dom-audiotrack-enabled
fn SetEnabled(&self, value: bool) {
- if let Some(list) = self.track_list.as_ref() {
+ if let Some(list) = self.track_list.borrow().as_ref() {
if let Some(idx) = list.find(self) {
list.set_enabled(idx, value);
}
diff --git a/components/script/dom/audiotracklist.rs b/components/script/dom/audiotracklist.rs
index e26f23cb2a8..5191221b7d9 100644
--- a/components/script/dom/audiotracklist.rs
+++ b/components/script/dom/audiotracklist.rs
@@ -104,9 +104,14 @@ impl AudioTrackList {
pub fn add(&self, track: &AudioTrack) {
self.tracks.borrow_mut().push(Dom::from_ref(track));
+ track.add_track_list(self);
}
pub fn clear(&self) {
+ self.tracks
+ .borrow()
+ .iter()
+ .for_each(|t| t.remove_track_list());
self.tracks.borrow_mut().clear();
}
}
diff --git a/components/script/dom/bindings/interface.rs b/components/script/dom/bindings/interface.rs
index 2514efcf063..2097769cb31 100644
--- a/components/script/dom/bindings/interface.rs
+++ b/components/script/dom/bindings/interface.rs
@@ -9,16 +9,13 @@ use crate::dom::bindings::codegen::PrototypeList;
use crate::dom::bindings::constant::{define_constants, ConstantSpec};
use crate::dom::bindings::conversions::{get_dom_class, DOM_OBJECT_SLOT};
use crate::dom::bindings::guard::Guard;
-use crate::dom::bindings::utils::{
- get_proto_or_iface_array, ProtoOrIfaceArray, DOM_PROTOTYPE_SLOT,
-};
+use crate::dom::bindings::utils::{ProtoOrIfaceArray, DOM_PROTOTYPE_SLOT};
use crate::script_runtime::JSContext as SafeJSContext;
use js::error::throw_type_error;
use js::glue::UncheckedUnwrapObject;
+use js::jsapi::GetWellKnownSymbol;
use js::jsapi::HandleObject as RawHandleObject;
-use js::jsapi::MutableHandleValue as RawMutableHandleValue;
use js::jsapi::{jsid, Class, ClassOps};
-use js::jsapi::{GetNonCCWObjectGlobal, GetWellKnownSymbol};
use js::jsapi::{JSAutoRealm, JSClass, JSContext, JSFunctionSpec, JSObject, JSFUN_CONSTRUCTOR};
use js::jsapi::{JSPropertySpec, JSString, JSTracer, JS_AtomizeAndPinString};
use js::jsapi::{JS_GetFunctionObject, JS_NewFunction, JS_NewGlobalObject};
@@ -28,10 +25,10 @@ use js::jsapi::{ObjectOps, OnNewGlobalHookOption, SymbolCode};
use js::jsapi::{TrueHandleValue, Value};
use js::jsapi::{JSPROP_PERMANENT, JSPROP_READONLY, JSPROP_RESOLVING};
use js::jsval::{JSVal, PrivateValue};
+use js::rust::wrappers::JS_FireOnNewGlobalObject;
use js::rust::wrappers::RUST_SYMBOL_TO_JSID;
use js::rust::wrappers::{JS_DefineProperty, JS_DefineProperty5};
use js::rust::wrappers::{JS_DefineProperty3, JS_DefineProperty4, JS_DefinePropertyById5};
-use js::rust::wrappers::{JS_FireOnNewGlobalObject, JS_GetPrototype};
use js::rust::wrappers::{JS_LinkConstructorAndPrototype, JS_NewObjectWithUniqueType};
use js::rust::{define_methods, define_properties, get_object_class};
use js::rust::{HandleObject, HandleValue, MutableHandleObject, RealmOptions};
@@ -102,7 +99,7 @@ impl InterfaceConstructorBehavior {
finalize: None,
call: Some(invalid_constructor),
construct: Some(invalid_constructor),
- hasInstance: Some(has_instance_hook),
+ hasInstance: None, // heycam/webidl#356
trace: None,
})
}
@@ -119,7 +116,7 @@ impl InterfaceConstructorBehavior {
finalize: None,
call: Some(non_new_constructor),
construct: Some(hook),
- hasInstance: Some(has_instance_hook),
+ hasInstance: None, // heycam/webidl#356
trace: None,
})
}
@@ -428,78 +425,6 @@ unsafe extern "C" fn fun_to_string_hook(
ret
}
-/// Hook for instanceof on interface objects.
-unsafe extern "C" fn has_instance_hook(
- cx: *mut JSContext,
- obj: RawHandleObject,
- value: RawMutableHandleValue,
- rval: *mut bool,
-) -> bool {
- let cx = SafeJSContext::from_ptr(cx);
- let obj_raw = HandleObject::from_raw(obj);
- let val_raw = HandleValue::from_raw(value.handle());
- match has_instance(cx, obj_raw, val_raw) {
- Ok(result) => {
- *rval = result;
- true
- },
- Err(()) => false,
- }
-}
-
-/// Return whether a value is an instance of a given prototype.
-/// <http://heycam.github.io/webidl/#es-interface-hasinstance>
-fn has_instance(
- cx: SafeJSContext,
- interface_object: HandleObject,
- value: HandleValue,
-) -> Result<bool, ()> {
- if !value.is_object() {
- // Step 1.
- return Ok(false);
- }
-
- rooted!(in(*cx) let mut value_out = value.to_object());
- rooted!(in(*cx) let mut value = value.to_object());
-
- unsafe {
- let js_class = get_object_class(interface_object.get());
- let object_class = &*(js_class as *const NonCallbackInterfaceObjectClass);
-
- if let Ok(dom_class) = get_dom_class(UncheckedUnwrapObject(
- value.get(),
- /* stopAtWindowProxy = */ 0,
- )) {
- if dom_class.interface_chain[object_class.proto_depth as usize] == object_class.proto_id
- {
- // Step 4.
- return Ok(true);
- }
- }
-
- // Step 2.
- let global = GetNonCCWObjectGlobal(interface_object.get());
- assert!(!global.is_null());
- let proto_or_iface_array = get_proto_or_iface_array(global);
- rooted!(in(*cx) let prototype = (*proto_or_iface_array)[object_class.proto_id as usize]);
- assert!(!prototype.is_null());
- // Step 3 only concern legacy callback interface objects (i.e. NodeFilter).
-
- while JS_GetPrototype(*cx, value.handle(), value_out.handle_mut()) {
- *value = *value_out;
- if value.is_null() {
- // Step 5.2.
- return Ok(false);
- } else if value.get() as *const _ == prototype.get() {
- // Step 5.3.
- return Ok(true);
- }
- }
- }
- // JS_GetPrototype threw an exception.
- Err(())
-}
-
fn create_unscopable_object(cx: SafeJSContext, names: &[&[u8]], mut rval: MutableHandleObject) {
assert!(!names.is_empty());
assert!(rval.is_null());
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs
index 217e66837bb..d2e1876b9d8 100644
--- a/components/script/dom/bindings/trace.rs
+++ b/components/script/dom/bindings/trace.rs
@@ -311,6 +311,15 @@ unsafe impl<T: JSTraceable> JSTraceable for VecDeque<T> {
}
}
+unsafe impl<T: JSTraceable + Eq + Hash> JSTraceable for indexmap::IndexSet<T> {
+ #[inline]
+ unsafe fn trace(&self, trc: *mut JSTracer) {
+ for e in self.iter() {
+ e.trace(trc);
+ }
+ }
+}
+
unsafe impl<A, B, C, D> JSTraceable for (A, B, C, D)
where
A: JSTraceable,
diff --git a/components/script/dom/create.rs b/components/script/dom/create.rs
index e992a8707dd..c45e4286992 100644
--- a/components/script/dom/create.rs
+++ b/components/script/dom/create.rs
@@ -189,14 +189,21 @@ fn create_html_element(
}
}
- // Steps 7.1-7.2
+ // Steps 7.1-7.3
let result = create_native_html_element(name.clone(), prefix, document, creator);
+ match is {
+ Some(is) => {
+ result.set_is(is);
+ result.set_custom_element_state(CustomElementState::Undefined);
+ },
+ None => {
+ if is_valid_custom_element_name(&*name.local) {
+ result.set_custom_element_state(CustomElementState::Undefined);
+ }
+ },
+ };
- // Step 7.3
- if is_valid_custom_element_name(&*name.local) || is.is_some() {
- result.set_custom_element_state(CustomElementState::Undefined);
- }
-
+ // Step 8
result
}
diff --git a/components/script/dom/customelementregistry.rs b/components/script/dom/customelementregistry.rs
index 7b4229835e2..392cf49cce5 100644
--- a/components/script/dom/customelementregistry.rs
+++ b/components/script/dom/customelementregistry.rs
@@ -448,6 +448,17 @@ impl CustomElementRegistryMethods for CustomElementRegistry {
// Step 6
promise
}
+ /// https://html.spec.whatwg.org/multipage/#dom-customelementregistry-upgrade
+ fn Upgrade(&self, node: &Node) {
+ // Spec says to make a list first and then iterate the list, but
+ // try-to-upgrade only queues upgrade reactions and doesn't itself
+ // modify the tree, so that's not an observable distinction.
+ node.traverse_preorder(ShadowIncluding::Yes).for_each(|n| {
+ if let Some(element) = n.downcast::<Element>() {
+ try_upgrade_element(element);
+ }
+ });
+ }
}
#[derive(Clone, JSTraceable, MallocSizeOf)]
@@ -581,7 +592,9 @@ pub fn upgrade_element(definition: Rc<CustomElementDefinition>, element: &Elemen
return;
}
- // Step 3
+ // Step 3 happens later to save having to undo it in an exception
+
+ // Step 4
for attr in element.attrs().iter() {
let local_name = attr.local_name().clone();
let value = DOMString::from(&**attr.value());
@@ -593,7 +606,7 @@ pub fn upgrade_element(definition: Rc<CustomElementDefinition>, element: &Elemen
);
}
- // Step 4
+ // Step 5
if element.is_connected() {
ScriptThread::enqueue_callback_reaction(
element,
@@ -602,44 +615,51 @@ pub fn upgrade_element(definition: Rc<CustomElementDefinition>, element: &Elemen
);
}
- // Step 5
+ // Step 6
definition
.construction_stack
.borrow_mut()
.push(ConstructionStackEntry::Element(DomRoot::from_ref(element)));
- // Step 7
+ // Steps 7-8, successful case
let result = run_upgrade_constructor(&definition.constructor, element);
+ // "regardless of whether the above steps threw an exception" step
definition.construction_stack.borrow_mut().pop();
- // Step 7 exception handling
+ // Step 8 exception handling
if let Err(error) = result {
- // Step 7.1
+ // Step 8.exception.1
element.set_custom_element_state(CustomElementState::Failed);
- // Step 7.2
+ // Step 8.exception.2 isn't needed since step 3 hasn't happened yet
+
+ // Step 8.exception.3
element.clear_reaction_queue();
- // Step 7.3
+ // Step 8.exception.4
let global = GlobalScope::current().expect("No current global");
let cx = global.get_cx();
unsafe {
throw_dom_exception(cx, &global, error);
report_pending_exception(*cx, true);
}
+
return;
}
- // Step 8
+ // TODO Step 9: "If element is a form-associated custom element..."
+
+ // Step 10
+
element.set_custom_element_state(CustomElementState::Custom);
- // Step 9
+ // Step 3
element.set_custom_element_definition(definition);
}
/// <https://html.spec.whatwg.org/multipage/#concept-upgrade-an-element>
-/// Steps 7.1-7.2
+/// Steps 8.1-8.3
#[allow(unsafe_code)]
fn run_upgrade_constructor(
constructor: &Rc<CustomElementConstructor>,
@@ -654,10 +674,12 @@ fn run_upgrade_constructor(
}
rooted!(in(*cx) let mut construct_result = ptr::null_mut::<JSObject>());
{
+ // Step 8.1 TODO when shadow DOM exists
+
// Go into the constructor's compartment
let _ac = JSAutoRealm::new(*cx, constructor.callback());
let args = HandleValueArray::new();
- // Step 7.1
+ // Step 8.2
if unsafe {
!Construct1(
*cx,
@@ -668,7 +690,7 @@ fn run_upgrade_constructor(
} {
return Err(Error::JSFailed);
}
- // Step 7.2
+ // Step 8.3
let mut same = false;
rooted!(in(*cx) let construct_result_val = ObjectValue(construct_result.get()));
if unsafe {
diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs
index 567d4fa6861..ad0e7445f0f 100644
--- a/components/script/dom/dedicatedworkerglobalscope.rs
+++ b/components/script/dom/dedicatedworkerglobalscope.rs
@@ -538,9 +538,8 @@ impl DedicatedWorkerGlobalScope {
let worker = self.worker.borrow().as_ref().unwrap().clone();
let global_scope = self.upcast::<GlobalScope>();
let pipeline_id = global_scope.pipeline_id();
- let origin = global_scope.origin().immutable().ascii_serialization();
let task = Box::new(task!(post_worker_message: move || {
- Worker::handle_message(worker, origin, data);
+ Worker::handle_message(worker, data);
}));
self.parent_sender
.send(CommonScriptMsg::Task(
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs
index 94c9c8603c4..572c18dc129 100644
--- a/components/script/dom/document.rs
+++ b/components/script/dom/document.rs
@@ -11,7 +11,6 @@ use crate::dom::bindings::callback::ExceptionHandling;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::BeforeUnloadEventBinding::BeforeUnloadEventBinding::BeforeUnloadEventMethods;
use crate::dom::bindings::codegen::Bindings::DocumentBinding;
-use crate::dom::bindings::codegen::Bindings::DocumentBinding::ElementCreationOptions;
use crate::dom::bindings::codegen::Bindings::DocumentBinding::{
DocumentMethods, DocumentReadyState,
};
@@ -25,7 +24,7 @@ use crate::dom::bindings::codegen::Bindings::TouchBinding::TouchMethods;
use crate::dom::bindings::codegen::Bindings::WindowBinding::{
FrameRequestCallback, ScrollBehavior, WindowMethods,
};
-use crate::dom::bindings::codegen::UnionTypes::NodeOrString;
+use crate::dom::bindings::codegen::UnionTypes::{NodeOrString, StringOrElementCreationOptions};
use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
use crate::dom::bindings::num::Finite;
@@ -371,6 +370,8 @@ pub struct Document {
page_showing: Cell<bool>,
/// Whether the document is salvageable.
salvageable: Cell<bool>,
+ /// Whether the document was aborted with an active parser
+ active_parser_was_aborted: Cell<bool>,
/// Whether the unload event has already been fired.
fired_unload: Cell<bool>,
/// List of responsive images
@@ -2265,6 +2266,7 @@ impl Document {
// Step 3.
if let Some(parser) = self.get_current_parser() {
+ self.active_parser_was_aborted.set(true);
parser.abort();
self.salvageable.set(false);
}
@@ -2813,6 +2815,7 @@ impl Document {
throw_on_dynamic_markup_insertion_counter: Cell::new(0),
page_showing: Cell::new(false),
salvageable: Cell::new(true),
+ active_parser_was_aborted: Cell::new(false),
fired_unload: Cell::new(false),
responsive_images: Default::default(),
redirect_count: Cell::new(0),
@@ -3622,7 +3625,7 @@ impl DocumentMethods for Document {
fn CreateElement(
&self,
mut local_name: DOMString,
- options: &ElementCreationOptions,
+ options: StringOrElementCreationOptions,
) -> Fallible<DomRoot<Element>> {
if xml_name_type(&local_name) == InvalidXMLName {
debug!("Not a valid element name");
@@ -3643,7 +3646,12 @@ impl DocumentMethods for Document {
};
let name = QualName::new(None, ns, LocalName::from(local_name));
- let is = options.is.as_ref().map(|is| LocalName::from(&**is));
+ let is = match options {
+ StringOrElementCreationOptions::String(_) => None,
+ StringOrElementCreationOptions::ElementCreationOptions(options) => {
+ options.is.as_ref().map(|is| LocalName::from(&**is))
+ },
+ };
Ok(Element::create(
name,
is,
@@ -3658,11 +3666,16 @@ impl DocumentMethods for Document {
&self,
namespace: Option<DOMString>,
qualified_name: DOMString,
- options: &ElementCreationOptions,
+ options: StringOrElementCreationOptions,
) -> Fallible<DomRoot<Element>> {
let (namespace, prefix, local_name) = validate_and_extract(namespace, &qualified_name)?;
let name = QualName::new(prefix, namespace, local_name);
- let is = options.is.as_ref().map(|is| LocalName::from(&**is));
+ let is = match options {
+ StringOrElementCreationOptions::String(_) => None,
+ StringOrElementCreationOptions::ElementCreationOptions(options) => {
+ options.is.as_ref().map(|is| LocalName::from(&**is))
+ },
+ };
Ok(Element::create(
name,
is,
@@ -4449,18 +4462,25 @@ impl DocumentMethods for Document {
return Ok(DomRoot::from_ref(self));
}
+ // Step 7
+ if self.active_parser_was_aborted.get() {
+ return Ok(DomRoot::from_ref(self));
+ }
+
// TODO: prompt to unload.
// TODO: set unload_event_start and unload_event_end
window_from_node(self).set_navigation_start();
- // Step 7
+ // Step 8
// TODO: https://github.com/servo/servo/issues/21937
if self.has_browsing_context() {
+ // spec says "stop document loading",
+ // which is a process that does more than just abort
self.abort();
}
- // Step 8
+ // Step 9
for node in self
.upcast::<Node>()
.traverse_preorder(ShadowIncluding::Yes)
@@ -4468,16 +4488,16 @@ impl DocumentMethods for Document {
node.upcast::<EventTarget>().remove_all_listeners();
}
- // Step 9
+ // Step 10
if self.window.Document() == DomRoot::from_ref(self) {
self.window.upcast::<EventTarget>().remove_all_listeners();
}
- // Step 10
+ // Step 11
// TODO: https://github.com/servo/servo/issues/21936
Node::replace_all(None, self.upcast::<Node>());
- // Step 11
+ // Step 12
if self.is_fully_active() {
let mut new_url = entry_responsible_document.url();
if entry_responsible_document != DomRoot::from_ref(self) {
@@ -4487,13 +4507,13 @@ impl DocumentMethods for Document {
self.set_url(new_url);
}
- // Step 12
+ // Step 13
// TODO: https://github.com/servo/servo/issues/21938
- // Step 13
+ // Step 14
self.set_quirks_mode(QuirksMode::NoQuirks);
- // Step 14
+ // Step 15
let resource_threads = self
.window
.upcast::<GlobalScope>()
@@ -4503,13 +4523,13 @@ impl DocumentMethods for Document {
DocumentLoader::new_with_threads(resource_threads, Some(self.url()));
ServoParser::parse_html_script_input(self, self.url());
- // Step 15
- self.ready_state.set(DocumentReadyState::Loading);
-
// Step 16
- // Handled when creating the parser in step 14
+ self.ready_state.set(DocumentReadyState::Loading);
// Step 17
+ // Handled when creating the parser in step 15
+
+ // Step 18
Ok(DomRoot::from_ref(self))
}
@@ -4541,8 +4561,8 @@ impl DocumentMethods for Document {
return Err(Error::InvalidState);
}
- if !self.is_active() {
- // Step 3.
+ // Step 3 - what specifies the is_active() part here?
+ if !self.is_active() || self.active_parser_was_aborted.get() {
return Ok(());
}
diff --git a/components/script/dom/domimplementation.rs b/components/script/dom/domimplementation.rs
index e86e7472a95..d12b18168f8 100644
--- a/components/script/dom/domimplementation.rs
+++ b/components/script/dom/domimplementation.rs
@@ -9,6 +9,7 @@ use crate::dom::bindings::codegen::Bindings::DocumentBinding::{
DocumentMethods, ElementCreationOptions,
};
use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
+use crate::dom::bindings::codegen::UnionTypes::StringOrElementCreationOptions;
use crate::dom::bindings::error::Fallible;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
@@ -105,10 +106,13 @@ impl DOMImplementationMethods for DOMImplementation {
let maybe_elem = if qname.is_empty() {
None
} else {
- let options = ElementCreationOptions { is: None };
+ let options =
+ StringOrElementCreationOptions::ElementCreationOptions(ElementCreationOptions {
+ is: None,
+ });
match doc
.upcast::<Document>()
- .CreateElementNS(maybe_namespace, qname, &options)
+ .CreateElementNS(maybe_namespace, qname, options)
{
Err(error) => return Err(error),
Ok(elem) => Some(elem),
diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs
index f6937427eda..04f8d948a24 100644
--- a/components/script/dom/globalscope.rs
+++ b/components/script/dom/globalscope.rs
@@ -24,6 +24,7 @@ use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
use crate::dom::eventsource::EventSource;
use crate::dom::eventtarget::EventTarget;
use crate::dom::file::File;
+use crate::dom::htmlscriptelement::ScriptId;
use crate::dom::messageevent::MessageEvent;
use crate::dom::messageport::MessagePort;
use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope;
@@ -32,6 +33,7 @@ use crate::dom::window::Window;
use crate::dom::workerglobalscope::WorkerGlobalScope;
use crate::dom::workletglobalscope::WorkletGlobalScope;
use crate::microtask::{Microtask, MicrotaskQueue};
+use crate::script_module::ModuleTree;
use crate::script_runtime::{CommonScriptMsg, JSContext as SafeJSContext, ScriptChan, ScriptPort};
use crate::script_thread::{MainThreadScriptChan, ScriptThread};
use crate::task::TaskCanceller;
@@ -119,6 +121,14 @@ pub struct GlobalScope {
/// Timers used by the Console API.
console_timers: DomRefCell<HashMap<DOMString, u64>>,
+ /// module map is used when importing JavaScript modules
+ /// https://html.spec.whatwg.org/multipage/#concept-settings-object-module-map
+ #[ignore_malloc_size_of = "mozjs"]
+ module_map: DomRefCell<HashMap<ServoUrl, Rc<ModuleTree>>>,
+
+ #[ignore_malloc_size_of = "mozjs"]
+ inline_module_map: DomRefCell<HashMap<ScriptId, Rc<ModuleTree>>>,
+
/// For providing instructions to an optional devtools server.
#[ignore_malloc_size_of = "channels are hard"]
devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
@@ -391,6 +401,8 @@ impl GlobalScope {
pipeline_id,
devtools_wants_updates: Default::default(),
console_timers: DomRefCell::new(Default::default()),
+ module_map: DomRefCell::new(Default::default()),
+ inline_module_map: DomRefCell::new(Default::default()),
devtools_chan,
mem_profiler_chan,
time_profiler_chan,
@@ -1357,6 +1369,24 @@ impl GlobalScope {
&self.consumed_rejections
}
+ pub fn set_module_map(&self, url: ServoUrl, module: ModuleTree) {
+ self.module_map.borrow_mut().insert(url, Rc::new(module));
+ }
+
+ pub fn get_module_map(&self) -> &DomRefCell<HashMap<ServoUrl, Rc<ModuleTree>>> {
+ &self.module_map
+ }
+
+ pub fn set_inline_module_map(&self, script_id: ScriptId, module: ModuleTree) {
+ self.inline_module_map
+ .borrow_mut()
+ .insert(script_id, Rc::new(module));
+ }
+
+ pub fn get_inline_module_map(&self) -> &DomRefCell<HashMap<ScriptId, Rc<ModuleTree>>> {
+ &self.inline_module_map
+ }
+
#[allow(unsafe_code)]
pub fn get_cx(&self) -> SafeJSContext {
unsafe { SafeJSContext::from_ptr(Runtime::get()) }
diff --git a/components/script/dom/headers.rs b/components/script/dom/headers.rs
index 61c6fc1f98e..a75b0292d6f 100644
--- a/components/script/dom/headers.rs
+++ b/components/script/dom/headers.rs
@@ -100,10 +100,20 @@ impl HeadersMethods for Headers {
combined_value.push(b',');
}
combined_value.extend(valid_value.iter().cloned());
- self.header_list.borrow_mut().insert(
- HeaderName::from_str(&valid_name).unwrap(),
- HeaderValue::from_bytes(&combined_value).unwrap(),
- );
+ match HeaderValue::from_bytes(&combined_value) {
+ Ok(value) => {
+ self.header_list
+ .borrow_mut()
+ .insert(HeaderName::from_str(&valid_name).unwrap(), value);
+ },
+ Err(_) => {
+ // can't add the header, but we don't need to panic the browser over it
+ warn!(
+ "Servo thinks \"{:?}\" is a valid HTTP header value but HeaderValue doesn't.",
+ combined_value
+ );
+ },
+ };
Ok(())
}
@@ -197,7 +207,7 @@ impl Headers {
for (name, value) in h.header_list.borrow().iter() {
self.Append(
ByteString::new(Vec::from(name.as_str())),
- ByteString::new(Vec::from(value.to_str().unwrap().as_bytes())),
+ ByteString::new(Vec::from(value.as_bytes())),
)?;
}
Ok(())
@@ -267,13 +277,13 @@ impl Headers {
.map_or(vec![], |v| v.as_bytes().to_owned())
}
- pub fn sort_header_list(&self) -> Vec<(String, String)> {
+ pub fn sort_header_list(&self) -> Vec<(String, Vec<u8>)> {
let borrowed_header_list = self.header_list.borrow();
let headers_iter = borrowed_header_list.iter();
let mut header_vec = vec![];
for (name, value) in headers_iter {
let name = name.as_str().to_owned();
- let value = value.to_str().unwrap().to_owned();
+ let value = value.as_bytes().to_vec();
let name_value = (name, value);
header_vec.push(name_value);
}
@@ -293,7 +303,7 @@ impl Iterable for Headers {
fn get_value_at_index(&self, n: u32) -> ByteString {
let sorted_header_vec = self.sort_header_list();
let value = sorted_header_vec[n as usize].1.clone();
- ByteString::new(value.into_bytes().to_vec())
+ ByteString::new(value)
}
fn get_key_at_index(&self, n: u32) -> ByteString {
@@ -345,40 +355,19 @@ pub fn is_forbidden_header_name(name: &str) -> bool {
}
// There is some unresolved confusion over the definition of a name and a value.
-// The fetch spec [1] defines a name as "a case-insensitive byte
-// sequence that matches the field-name token production. The token
-// productions are viewable in [2]." A field-name is defined as a
-// token, which is defined in [3].
-// ISSUE 1:
-// It defines a value as "a byte sequence that matches the field-content token production."
-// To note, there is a difference between field-content and
-// field-value (which is made up of field-content and obs-fold). The
-// current definition does not allow for obs-fold (which are white
-// space and newlines) in values. So perhaps a value should be defined
-// as "a byte sequence that matches the field-value token production."
-// However, this would then allow values made up entirely of white space and newlines.
-// RELATED ISSUE 2:
-// According to a previously filed Errata ID: 4189 in [4], "the
-// specified field-value rule does not allow single field-vchar
-// surrounded by whitespace anywhere". They provided a fix for the
-// field-content production, but ISSUE 1 has still not been resolved.
-// The production definitions likely need to be re-written.
-// [1] https://fetch.spec.whatwg.org/#concept-header-value
-// [2] https://tools.ietf.org/html/rfc7230#section-3.2
-// [3] https://tools.ietf.org/html/rfc7230#section-3.2.6
-// [4] https://www.rfc-editor.org/errata_search.php?rfc=7230
//
-// As of December 2019 WHATWG, isn't even using grammar productions for value;
+// As of December 2019, WHATWG has no formal grammar production for value;
// https://fetch.spec.whatg.org/#concept-header-value just says not to have
-// newlines, nulls, or leading/trailing whitespace.
+// newlines, nulls, or leading/trailing whitespace. It even allows
+// octets that aren't a valid UTF-8 encoding, and WPT tests reflect this.
+// The HeaderValue class does not fully reflect this, so headers
+// containing bytes with values 1..31 or 127 can't be created, failing
+// WPT tests but probably not affecting anything important on the real Internet.
fn validate_name_and_value(name: ByteString, value: ByteString) -> Fallible<(String, Vec<u8>)> {
let valid_name = validate_name(name)?;
-
- // this is probably out of date
- if !is_field_content(&value) {
- return Err(Error::Type("Value is not valid".to_string()));
+ if !is_legal_header_value(&value) {
+ return Err(Error::Type("Header value is not valid".to_string()));
}
-
Ok((valid_name, value.into()))
}
@@ -431,47 +420,40 @@ fn is_field_name(name: &ByteString) -> bool {
is_token(&*name)
}
-// https://tools.ietf.org/html/rfc7230#section-3.2
-// http://www.rfc-editor.org/errata_search.php?rfc=7230
-// Errata ID: 4189
-// field-content = field-vchar [ 1*( SP / HTAB / field-vchar )
-// field-vchar ]
-fn is_field_content(value: &ByteString) -> bool {
+// https://fetch.spec.whatg.org/#concept-header-value
+fn is_legal_header_value(value: &ByteString) -> bool {
let value_len = value.len();
-
if value_len == 0 {
- return false;
- }
- if !is_field_vchar(value[0]) {
- return false;
- }
-
- if value_len > 2 {
- for &ch in &value[1..value_len - 1] {
- if !is_field_vchar(ch) && !is_space(ch) && !is_htab(ch) {
- return false;
- }
+ return true;
+ }
+ match value[0] {
+ b' ' | b'\t' => return false,
+ _ => {},
+ };
+ match value[value_len - 1] {
+ b' ' | b'\t' => return false,
+ _ => {},
+ };
+ for &ch in &value[..] {
+ match ch {
+ b'\0' | b'\n' | b'\r' => return false,
+ _ => {},
}
}
-
- if !is_field_vchar(value[value_len - 1]) {
- return false;
- }
-
- return true;
-}
-
-fn is_space(x: u8) -> bool {
- x == b' '
-}
-
-fn is_htab(x: u8) -> bool {
- x == b'\t'
-}
-
-// https://tools.ietf.org/html/rfc7230#section-3.2
-fn is_field_vchar(x: u8) -> bool {
- is_vchar(x) || is_obs_text(x)
+ true
+ // If accepting non-UTF8 header values causes breakage,
+ // removing the above "true" and uncommenting the below code
+ // would ameliorate it while still accepting most reasonable headers:
+ //match str::from_utf8(value) {
+ // Ok(_) => true,
+ // Err(_) => {
+ // warn!(
+ // "Rejecting spec-legal but non-UTF8 header value: {:?}",
+ // value
+ // );
+ // false
+ // },
+ // }
}
// https://tools.ietf.org/html/rfc5234#appendix-B.1
diff --git a/components/script/dom/htmlbuttonelement.rs b/components/script/dom/htmlbuttonelement.rs
index 1406ceb870b..b7c7da1e9bf 100755
--- a/components/script/dom/htmlbuttonelement.rs
+++ b/components/script/dom/htmlbuttonelement.rs
@@ -34,7 +34,6 @@ enum ButtonType {
Submit,
Reset,
Button,
- Menu,
}
#[dom_struct]
@@ -42,6 +41,7 @@ pub struct HTMLButtonElement {
htmlelement: HTMLElement,
button_type: Cell<ButtonType>,
form_owner: MutNullableDom<HTMLFormElement>,
+ labels_node_list: MutNullableDom<NodeList>,
}
impl HTMLButtonElement {
@@ -59,6 +59,7 @@ impl HTMLButtonElement {
),
button_type: Cell::new(ButtonType::Submit),
form_owner: Default::default(),
+ labels_node_list: Default::default(),
}
}
@@ -97,7 +98,7 @@ impl HTMLButtonElementMethods for HTMLButtonElement {
}
// https://html.spec.whatwg.org/multipage/#dom-button-type
- make_enumerated_getter!(Type, "type", "submit", "reset" | "button" | "menu");
+ make_enumerated_getter!(Type, "type", "submit", "reset" | "button");
// https://html.spec.whatwg.org/multipage/#dom-button-type
make_setter!(SetType, "type");
@@ -150,9 +151,7 @@ impl HTMLButtonElementMethods for HTMLButtonElement {
make_setter!(SetValue, "value");
// https://html.spec.whatwg.org/multipage/#dom-lfe-labels
- fn Labels(&self) -> DomRoot<NodeList> {
- self.upcast::<HTMLElement>().labels()
- }
+ make_labels_getter!(Labels, labels_node_list);
}
impl HTMLButtonElement {
@@ -216,7 +215,6 @@ impl VirtualMethods for HTMLButtonElement {
let value = match &**attr.value() {
"reset" => ButtonType::Reset,
"button" => ButtonType::Button,
- "menu" => ButtonType::Menu,
_ => ButtonType::Submit,
};
self.button_type.set(value);
diff --git a/components/script/dom/htmlelement.rs b/components/script/dom/htmlelement.rs
index 7bec738f382..c5b7e6d0251 100644
--- a/components/script/dom/htmlelement.rs
+++ b/components/script/dom/htmlelement.rs
@@ -4,10 +4,10 @@
use crate::dom::activation::{synthetic_click_activation, ActivationSource};
use crate::dom::attr::Attr;
-use crate::dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
use crate::dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
use crate::dom::bindings::codegen::Bindings::HTMLElementBinding;
use crate::dom::bindings::codegen::Bindings::HTMLElementBinding::HTMLElementMethods;
+use crate::dom::bindings::codegen::Bindings::HTMLLabelElementBinding::HTMLLabelElementMethods;
use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeBinding::NodeMethods;
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use crate::dom::bindings::error::{Error, ErrorResult};
@@ -29,7 +29,6 @@ use crate::dom::htmlinputelement::{HTMLInputElement, InputType};
use crate::dom::htmllabelelement::HTMLLabelElement;
use crate::dom::node::{document_from_node, window_from_node};
use crate::dom::node::{BindContext, Node, NodeFlags, ShadowIncluding};
-use crate::dom::nodelist::NodeList;
use crate::dom::text::Text;
use crate::dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
@@ -677,43 +676,48 @@ impl HTMLElement {
}
// https://html.spec.whatwg.org/multipage/#dom-lfe-labels
- pub fn labels(&self) -> DomRoot<NodeList> {
- debug_assert!(self.is_labelable_element());
-
+ // This gets the nth label in tree order.
+ pub fn label_at(&self, index: u32) -> Option<DomRoot<Node>> {
let element = self.upcast::<Element>();
- let window = window_from_node(element);
-
- // Traverse ancestors for implicitly associated <label> elements
- // https://html.spec.whatwg.org/multipage/#the-label-element:attr-label-for-4
- let ancestors = self
- .upcast::<Node>()
- .ancestors()
- .filter_map(DomRoot::downcast::<HTMLElement>)
- // If we reach a labelable element, we have a guarantee no ancestors above it
- // will be a label for this HTMLElement
- .take_while(|elem| !elem.is_labelable_element())
- .filter_map(DomRoot::downcast::<HTMLLabelElement>)
- .filter(|elem| !elem.upcast::<Element>().has_attribute(&local_name!("for")))
- .filter(|elem| elem.first_labelable_descendant().as_deref() == Some(self))
- .map(DomRoot::upcast::<Node>);
- let id = element.Id();
- let id = match &id as &str {
- "" => return NodeList::new_simple_list(&window, ancestors),
- id => id,
- };
-
- // Traverse entire tree for <label> elements with `for` attribute matching `id`
+ // Traverse entire tree for <label> elements that have
+ // this as their control.
+ // There is room for performance optimization, as we don't need
+ // the actual result of GetControl, only whether the result
+ // would match self.
+ // (Even more room for performance optimization: do what
+ // nodelist ChildrenList does and keep a mutation-aware cursor
+ // around; this may be hard since labels need to keep working
+ // even as they get detached into a subtree and reattached to
+ // a document.)
let root_element = element.root_element();
let root_node = root_element.upcast::<Node>();
- let children = root_node
+ root_node
.traverse_preorder(ShadowIncluding::No)
- .filter_map(DomRoot::downcast::<Element>)
- .filter(|elem| elem.is::<HTMLLabelElement>())
- .filter(|elem| elem.get_string_attribute(&local_name!("for")) == id)
- .map(DomRoot::upcast::<Node>);
+ .filter_map(DomRoot::downcast::<HTMLLabelElement>)
+ .filter(|elem| match elem.GetControl() {
+ Some(control) => &*control == self,
+ _ => false,
+ })
+ .nth(index as usize)
+ .map(|n| DomRoot::from_ref(n.upcast::<Node>()))
+ }
- NodeList::new_simple_list(&window, children.chain(ancestors))
+ // https://html.spec.whatwg.org/multipage/#dom-lfe-labels
+ // This counts the labels of the element, to support NodeList::Length
+ pub fn labels_count(&self) -> u32 {
+ // see label_at comments about performance
+ let element = self.upcast::<Element>();
+ let root_element = element.root_element();
+ let root_node = root_element.upcast::<Node>();
+ root_node
+ .traverse_preorder(ShadowIncluding::No)
+ .filter_map(DomRoot::downcast::<HTMLLabelElement>)
+ .filter(|elem| match elem.GetControl() {
+ Some(control) => &*control == self,
+ _ => false,
+ })
+ .count() as u32
}
}
diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs
index 9018f05813a..90058f303a9 100755
--- a/components/script/dom/htmlinputelement.rs
+++ b/components/script/dom/htmlinputelement.rs
@@ -35,7 +35,7 @@ use crate::dom::htmlformelement::{ResetFrom, SubmittedFrom};
use crate::dom::keyboardevent::KeyboardEvent;
use crate::dom::mouseevent::MouseEvent;
use crate::dom::node::{document_from_node, window_from_node};
-use crate::dom::node::{BindContext, Node, NodeDamage, UnbindContext};
+use crate::dom::node::{BindContext, CloneChildrenFlag, Node, NodeDamage, UnbindContext};
use crate::dom::nodelist::NodeList;
use crate::dom::textcontrol::{TextControlElement, TextControlSelection};
use crate::dom::validation::Validatable;
@@ -46,7 +46,6 @@ use crate::textinput::KeyReaction::{
};
use crate::textinput::Lines::Single;
use crate::textinput::{Direction, SelectionDirection, TextInput, UTF16CodeUnits, UTF8Bytes};
-use caseless::compatibility_caseless_match_str;
use dom_struct::dom_struct;
use embedder_traits::FilterPattern;
use encoding_rs::Encoding;
@@ -235,6 +234,7 @@ pub struct HTMLInputElement {
filelist: MutNullableDom<FileList>,
form_owner: MutNullableDom<HTMLFormElement>,
+ labels_node_list: MutNullableDom<NodeList>,
}
#[derive(JSTraceable)]
@@ -304,6 +304,7 @@ impl HTMLInputElement {
value_dirty: Cell::new(false),
filelist: MutNullableDom::new(None),
form_owner: Default::default(),
+ labels_node_list: MutNullableDom::new(None),
}
}
@@ -792,12 +793,18 @@ impl HTMLInputElementMethods for HTMLInputElement {
}
// https://html.spec.whatwg.org/multipage/#dom-lfe-labels
- fn Labels(&self) -> DomRoot<NodeList> {
+ // Different from make_labels_getter because this one
+ // conditionally returns null.
+ fn GetLabels(&self) -> Option<DomRoot<NodeList>> {
if self.input_type() == InputType::Hidden {
- let window = window_from_node(self);
- NodeList::empty(&window)
+ None
} else {
- self.upcast::<HTMLElement>().labels()
+ Some(self.labels_node_list.or_init(|| {
+ NodeList::new_labels_list(
+ self.upcast::<Node>().owner_doc().window(),
+ self.upcast::<HTMLElement>(),
+ )
+ }))
}
}
@@ -917,7 +924,7 @@ fn in_same_group(
// TODO Both a and b are in the same home subtree.
other.form_owner().as_deref() == owner &&
match (other.radio_group_name(), group) {
- (Some(ref s1), Some(s2)) => compatibility_caseless_match_str(s1, s2) && s2 != &atom!(""),
+ (Some(ref s1), Some(s2)) => s1 == s2 && s2 != &atom!(""),
_ => false
}
}
@@ -1659,6 +1666,26 @@ impl VirtualMethods for HTMLInputElement {
}
}
}
+
+ // https://html.spec.whatwg.org/multipage/#the-input-element%3Aconcept-node-clone-ext
+ fn cloning_steps(
+ &self,
+ copy: &Node,
+ maybe_doc: Option<&Document>,
+ clone_children: CloneChildrenFlag,
+ ) {
+ if let Some(ref s) = self.super_type() {
+ s.cloning_steps(copy, maybe_doc, clone_children);
+ }
+ let elem = copy.downcast::<HTMLInputElement>().unwrap();
+ elem.value_dirty.set(self.value_dirty.get());
+ elem.checked_changed.set(self.checked_changed.get());
+ elem.upcast::<Element>()
+ .set_state(ElementState::IN_CHECKED_STATE, self.Checked());
+ elem.textinput
+ .borrow_mut()
+ .set_content(self.textinput.borrow().get_content());
+ }
}
impl FormControl for HTMLInputElement {
diff --git a/components/script/dom/htmllabelelement.rs b/components/script/dom/htmllabelelement.rs
index 0a1401d6283..cca8e0f03c9 100644
--- a/components/script/dom/htmllabelelement.rs
+++ b/components/script/dom/htmllabelelement.rs
@@ -4,8 +4,11 @@
use crate::dom::activation::{synthetic_click_activation, Activatable, ActivationSource};
use crate::dom::attr::Attr;
+use crate::dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
+use crate::dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
use crate::dom::bindings::codegen::Bindings::HTMLLabelElementBinding;
use crate::dom::bindings::codegen::Bindings::HTMLLabelElementBinding::HTMLLabelElementMethods;
+use crate::dom::bindings::codegen::Bindings::NodeBinding::{GetRootNodeOptions, NodeMethods};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
@@ -15,7 +18,7 @@ use crate::dom::event::Event;
use crate::dom::eventtarget::EventTarget;
use crate::dom::htmlelement::HTMLElement;
use crate::dom::htmlformelement::{FormControl, FormControlElementHelpers, HTMLFormElement};
-use crate::dom::node::{document_from_node, Node, ShadowIncluding};
+use crate::dom::node::{Node, ShadowIncluding};
use crate::dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix};
@@ -99,10 +102,6 @@ impl HTMLLabelElementMethods for HTMLLabelElement {
// https://html.spec.whatwg.org/multipage/#dom-label-control
fn GetControl(&self) -> Option<DomRoot<HTMLElement>> {
- if !self.upcast::<Node>().is_in_doc() {
- return None;
- }
-
let for_attr = match self
.upcast::<Element>()
.get_attribute(&ns!(), &local_name!("for"))
@@ -111,13 +110,40 @@ impl HTMLLabelElementMethods for HTMLLabelElement {
None => return self.first_labelable_descendant(),
};
- let for_value = for_attr.value();
- document_from_node(self)
- .get_element_by_id(for_value.as_atom())
- .and_then(DomRoot::downcast::<HTMLElement>)
- .into_iter()
- .filter(|e| e.is_labelable_element())
- .next()
+ let for_value = for_attr.Value();
+
+ // "If the attribute is specified and there is an element in the tree
+ // whose ID is equal to the value of the for attribute, and the first
+ // such element in tree order is a labelable element, then that
+ // element is the label element's labeled control."
+ // Two subtle points here: we need to search the _tree_, which is
+ // not necessarily the document if we're detached from the document,
+ // and we only consider one element even if a later element with
+ // the same ID is labelable.
+
+ let maybe_found = self
+ .upcast::<Node>()
+ .GetRootNode(&GetRootNodeOptions::empty())
+ .traverse_preorder(ShadowIncluding::No)
+ .find_map(|e| {
+ if let Some(htmle) = e.downcast::<HTMLElement>() {
+ if htmle.upcast::<Element>().Id() == for_value {
+ Some(DomRoot::from_ref(htmle))
+ } else {
+ None
+ }
+ } else {
+ None
+ }
+ });
+ // We now have the element that we would return, but only return it
+ // if it's labelable.
+ if let Some(ref maybe_labelable) = maybe_found {
+ if maybe_labelable.is_labelable_element() {
+ return maybe_found;
+ }
+ }
+ None
}
}
diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs
index 91acd4b97e7..cb93a77a90d 100644
--- a/components/script/dom/htmlmediaelement.rs
+++ b/components/script/dom/htmlmediaelement.rs
@@ -2362,6 +2362,7 @@ impl HTMLMediaElementMethods for HTMLMediaElement {
label,
language,
TextTrackMode::Hidden,
+ None,
);
// Step 3 & 4
self.TextTracks().add(&track);
diff --git a/components/script/dom/htmlmeterelement.rs b/components/script/dom/htmlmeterelement.rs
index a627d9aee93..780080a6a65 100644
--- a/components/script/dom/htmlmeterelement.rs
+++ b/components/script/dom/htmlmeterelement.rs
@@ -6,7 +6,7 @@ use crate::dom::bindings::codegen::Bindings::HTMLMeterElementBinding::{
self, HTMLMeterElementMethods,
};
use crate::dom::bindings::inheritance::Castable;
-use crate::dom::bindings::root::DomRoot;
+use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::document::Document;
use crate::dom::htmlelement::HTMLElement;
use crate::dom::node::Node;
@@ -17,6 +17,7 @@ use html5ever::{LocalName, Prefix};
#[dom_struct]
pub struct HTMLMeterElement {
htmlelement: HTMLElement,
+ labels_node_list: MutNullableDom<NodeList>,
}
impl HTMLMeterElement {
@@ -27,6 +28,7 @@ impl HTMLMeterElement {
) -> HTMLMeterElement {
HTMLMeterElement {
htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
+ labels_node_list: MutNullableDom::new(None),
}
}
@@ -48,7 +50,5 @@ impl HTMLMeterElement {
impl HTMLMeterElementMethods for HTMLMeterElement {
// https://html.spec.whatwg.org/multipage/#dom-lfe-labels
- fn Labels(&self) -> DomRoot<NodeList> {
- self.upcast::<HTMLElement>().labels()
- }
+ make_labels_getter!(Labels, labels_node_list);
}
diff --git a/components/script/dom/htmloptionelement.rs b/components/script/dom/htmloptionelement.rs
index 3893aea95ab..4ebb2860a79 100644
--- a/components/script/dom/htmloptionelement.rs
+++ b/components/script/dom/htmloptionelement.rs
@@ -8,12 +8,14 @@ use crate::dom::bindings::codegen::Bindings::HTMLOptionElementBinding;
use crate::dom::bindings::codegen::Bindings::HTMLOptionElementBinding::HTMLOptionElementMethods;
use crate::dom::bindings::codegen::Bindings::HTMLSelectElementBinding::HTMLSelectElementBinding::HTMLSelectElementMethods;
use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
+use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
+use crate::dom::bindings::error::Fallible;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::characterdata::CharacterData;
use crate::dom::document::Document;
-use crate::dom::element::{AttributeMutation, Element};
+use crate::dom::element::{AttributeMutation, CustomElementCreationMode, Element, ElementCreator};
use crate::dom::htmlelement::HTMLElement;
use crate::dom::htmlformelement::HTMLFormElement;
use crate::dom::htmloptgroupelement::HTMLOptGroupElement;
@@ -22,8 +24,9 @@ use crate::dom::htmlselectelement::HTMLSelectElement;
use crate::dom::node::{BindContext, Node, ShadowIncluding, UnbindContext};
use crate::dom::text::Text;
use crate::dom::virtualmethods::VirtualMethods;
+use crate::dom::window::Window;
use dom_struct::dom_struct;
-use html5ever::{LocalName, Prefix};
+use html5ever::{LocalName, Prefix, QualName};
use std::cell::Cell;
use style::element_state::ElementState;
use style::str::{split_html_space_chars, str_join};
@@ -72,6 +75,37 @@ impl HTMLOptionElement {
)
}
+ // https://html.spec.whatwg.org/multipage/#dom-option
+ pub fn Option(
+ window: &Window,
+ text: DOMString,
+ value: Option<DOMString>,
+ default_selected: bool,
+ selected: bool,
+ ) -> Fallible<DomRoot<HTMLOptionElement>> {
+ let element = Element::create(
+ QualName::new(None, ns!(html), local_name!("option")),
+ None,
+ &window.Document(),
+ ElementCreator::ScriptCreated,
+ CustomElementCreationMode::Synchronous,
+ );
+
+ let option = DomRoot::downcast::<HTMLOptionElement>(element).unwrap();
+
+ if !text.is_empty() {
+ option.upcast::<Node>().SetTextContent(Some(text))
+ }
+
+ if let Some(val) = value {
+ option.SetValue(val)
+ }
+
+ option.SetDefaultSelected(default_selected);
+ option.set_selectedness(selected);
+ Ok(option)
+ }
+
pub fn set_selectedness(&self, selected: bool) {
self.selectedness.set(selected);
}
diff --git a/components/script/dom/htmloutputelement.rs b/components/script/dom/htmloutputelement.rs
index 65b9ae554c8..9314672d3d0 100644
--- a/components/script/dom/htmloutputelement.rs
+++ b/components/script/dom/htmloutputelement.rs
@@ -22,6 +22,7 @@ use html5ever::{LocalName, Prefix};
pub struct HTMLOutputElement {
htmlelement: HTMLElement,
form_owner: MutNullableDom<HTMLFormElement>,
+ labels_node_list: MutNullableDom<NodeList>,
}
impl HTMLOutputElement {
@@ -33,6 +34,7 @@ impl HTMLOutputElement {
HTMLOutputElement {
htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
form_owner: Default::default(),
+ labels_node_list: Default::default(),
}
}
@@ -65,9 +67,7 @@ impl HTMLOutputElementMethods for HTMLOutputElement {
}
// https://html.spec.whatwg.org/multipage/#dom-lfe-labels
- fn Labels(&self) -> DomRoot<NodeList> {
- self.upcast::<HTMLElement>().labels()
- }
+ make_labels_getter!(Labels, labels_node_list);
}
impl VirtualMethods for HTMLOutputElement {
diff --git a/components/script/dom/htmlprogresselement.rs b/components/script/dom/htmlprogresselement.rs
index 26ee952b2c9..0c4789fdc14 100644
--- a/components/script/dom/htmlprogresselement.rs
+++ b/components/script/dom/htmlprogresselement.rs
@@ -6,7 +6,7 @@ use crate::dom::bindings::codegen::Bindings::HTMLProgressElementBinding::{
self, HTMLProgressElementMethods,
};
use crate::dom::bindings::inheritance::Castable;
-use crate::dom::bindings::root::DomRoot;
+use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::document::Document;
use crate::dom::htmlelement::HTMLElement;
use crate::dom::node::Node;
@@ -17,6 +17,7 @@ use html5ever::{LocalName, Prefix};
#[dom_struct]
pub struct HTMLProgressElement {
htmlelement: HTMLElement,
+ labels_node_list: MutNullableDom<NodeList>,
}
impl HTMLProgressElement {
@@ -27,6 +28,7 @@ impl HTMLProgressElement {
) -> HTMLProgressElement {
HTMLProgressElement {
htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
+ labels_node_list: MutNullableDom::new(None),
}
}
@@ -48,7 +50,5 @@ impl HTMLProgressElement {
impl HTMLProgressElementMethods for HTMLProgressElement {
// https://html.spec.whatwg.org/multipage/#dom-lfe-labels
- fn Labels(&self) -> DomRoot<NodeList> {
- self.upcast::<HTMLElement>().labels()
- }
+ make_labels_getter!(Labels, labels_node_list);
}
diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs
index fdeca285e30..3a55590c81d 100644
--- a/components/script/dom/htmlscriptelement.rs
+++ b/components/script/dom/htmlscriptelement.rs
@@ -12,6 +12,7 @@ use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::{Dom, DomRoot};
+use crate::dom::bindings::settings_stack::AutoEntryScript;
use crate::dom::bindings::str::{DOMString, USVString};
use crate::dom::document::Document;
use crate::dom::element::{
@@ -27,6 +28,8 @@ use crate::dom::performanceresourcetiming::InitiatorType;
use crate::dom::virtualmethods::VirtualMethods;
use crate::fetch::create_a_potential_CORS_request;
use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener};
+use crate::script_module::fetch_inline_module_script;
+use crate::script_module::{fetch_external_module_script, ModuleOwner};
use content_security_policy as csp;
use dom_struct::dom_struct;
use encoding_rs::Encoding;
@@ -35,7 +38,7 @@ use ipc_channel::ipc;
use ipc_channel::router::ROUTER;
use js::jsval::UndefinedValue;
use msg::constellation_msg::PipelineId;
-use net_traits::request::{CorsSettings, Destination, Referrer, RequestBuilder};
+use net_traits::request::{CorsSettings, CredentialsMode, Destination, Referrer, RequestBuilder};
use net_traits::ReferrerPolicy;
use net_traits::{FetchMetadata, FetchResponseListener, Metadata, NetworkError};
use net_traits::{ResourceFetchTiming, ResourceTimingType};
@@ -51,6 +54,10 @@ use std::sync::{Arc, Mutex};
use style::str::{StaticStringVec, HTML_SPACE_CHARACTERS};
use uuid::Uuid;
+/// An unique id for script element.
+#[derive(Clone, Copy, Debug, Eq, Hash, JSTraceable, PartialEq)]
+pub struct ScriptId(Uuid);
+
#[dom_struct]
pub struct HTMLScriptElement {
htmlelement: HTMLElement,
@@ -71,6 +78,10 @@ pub struct HTMLScriptElement {
/// Track line line_number
line_number: u64,
+
+ /// Unique id for each script element
+ #[ignore_malloc_size_of = "Defined in uuid"]
+ id: ScriptId,
}
impl HTMLScriptElement {
@@ -81,6 +92,7 @@ impl HTMLScriptElement {
creator: ElementCreator,
) -> HTMLScriptElement {
HTMLScriptElement {
+ id: ScriptId(Uuid::new_v4()),
htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
already_started: Cell::new(false),
parser_inserted: Cell::new(creator.is_parser_created()),
@@ -105,11 +117,15 @@ impl HTMLScriptElement {
HTMLScriptElementBinding::Wrap,
)
}
+
+ pub fn get_script_id(&self) -> ScriptId {
+ self.id.clone()
+ }
}
/// Supported script types as defined by
/// <https://html.spec.whatwg.org/multipage/#javascript-mime-type>.
-static SCRIPT_JS_MIMES: StaticStringVec = &[
+pub static SCRIPT_JS_MIMES: StaticStringVec = &[
"application/ecmascript",
"application/javascript",
"application/x-ecmascript",
@@ -143,7 +159,7 @@ pub struct ScriptOrigin {
}
impl ScriptOrigin {
- fn internal(text: DOMString, url: ServoUrl, type_: ScriptType) -> ScriptOrigin {
+ pub fn internal(text: DOMString, url: ServoUrl, type_: ScriptType) -> ScriptOrigin {
ScriptOrigin {
text: text,
url: url,
@@ -152,7 +168,7 @@ impl ScriptOrigin {
}
}
- fn external(text: DOMString, url: ServoUrl, type_: ScriptType) -> ScriptOrigin {
+ pub fn external(text: DOMString, url: ServoUrl, type_: ScriptType) -> ScriptOrigin {
ScriptOrigin {
text: text,
url: url,
@@ -160,6 +176,10 @@ impl ScriptOrigin {
type_,
}
}
+
+ pub fn text(&self) -> DOMString {
+ self.text.clone()
+ }
}
pub type ScriptResult = Result<ScriptOrigin, NetworkError>;
@@ -427,7 +447,10 @@ impl HTMLScriptElement {
return;
}
- // TODO: Step 12: nomodule content attribute
+ // Step 12
+ if element.has_attribute(&local_name!("nomodule")) && script_type == ScriptType::Classic {
+ return;
+ }
// Step 13.
if !element.has_attribute(&local_name!("src")) &&
@@ -441,23 +464,25 @@ impl HTMLScriptElement {
}
// Step 14.
- let for_attribute = element.get_attribute(&ns!(), &local_name!("for"));
- let event_attribute = element.get_attribute(&ns!(), &local_name!("event"));
- match (for_attribute, event_attribute) {
- (Some(ref for_attribute), Some(ref event_attribute)) => {
- let for_value = for_attribute.value().to_ascii_lowercase();
- let for_value = for_value.trim_matches(HTML_SPACE_CHARACTERS);
- if for_value != "window" {
- return;
- }
+ if script_type == ScriptType::Classic {
+ let for_attribute = element.get_attribute(&ns!(), &local_name!("for"));
+ let event_attribute = element.get_attribute(&ns!(), &local_name!("event"));
+ match (for_attribute, event_attribute) {
+ (Some(ref for_attribute), Some(ref event_attribute)) => {
+ let for_value = for_attribute.value().to_ascii_lowercase();
+ let for_value = for_value.trim_matches(HTML_SPACE_CHARACTERS);
+ if for_value != "window" {
+ return;
+ }
- let event_value = event_attribute.value().to_ascii_lowercase();
- let event_value = event_value.trim_matches(HTML_SPACE_CHARACTERS);
- if event_value != "onload" && event_value != "onload()" {
- return;
- }
- },
- (_, _) => (),
+ let event_value = event_attribute.value().to_ascii_lowercase();
+ let event_value = event_value.trim_matches(HTML_SPACE_CHARACTERS);
+ if event_value != "onload" && event_value != "onload()" {
+ return;
+ }
+ },
+ (_, _) => (),
+ }
}
// Step 15.
@@ -469,7 +494,18 @@ impl HTMLScriptElement {
// Step 16.
let cors_setting = cors_setting_for_element(element);
- // TODO: Step 17: Module script credentials mode.
+ // Step 17.
+ let credentials_mode = match script_type {
+ ScriptType::Classic => None,
+ ScriptType::Module => Some(reflect_cross_origin_attribute(element).map_or(
+ CredentialsMode::CredentialsSameOrigin,
+ |attr| match &*attr {
+ "use-credentials" => CredentialsMode::Include,
+ "anonymous" => CredentialsMode::CredentialsSameOrigin,
+ _ => CredentialsMode::CredentialsSameOrigin,
+ },
+ )),
+ };
// TODO: Step 18: Nonce.
@@ -514,6 +550,7 @@ impl HTMLScriptElement {
},
};
+ // Step 24.6.
match script_type {
ScriptType::Classic => {
// Preparation for step 26.
@@ -555,50 +592,69 @@ impl HTMLScriptElement {
}
},
ScriptType::Module => {
- warn!(
- "{} is a module script. It should be fixed after #23545 landed.",
- url.clone()
+ fetch_external_module_script(
+ ModuleOwner::Window(Trusted::new(self)),
+ url.clone(),
+ Destination::Script,
+ integrity_metadata.to_owned(),
+ credentials_mode.unwrap(),
);
- self.global().issue_page_warning(&format!(
- "Module scripts are not supported; {} will not be executed.",
- url.clone()
- ));
+
+ if !r#async && was_parser_inserted {
+ doc.add_deferred_script(self);
+ } else if !r#async && !self.non_blocking.get() {
+ doc.push_asap_in_order_script(self);
+ } else {
+ doc.add_asap_script(self);
+ };
},
}
} else {
// Step 25.
assert!(!text.is_empty());
- // Step 25-1.
+ // Step 25-1. & 25-2.
let result = Ok(ScriptOrigin::internal(
text.clone(),
base_url.clone(),
script_type.clone(),
));
- // TODO: Step 25-2.
- if let ScriptType::Module = script_type {
- warn!(
- "{} is a module script. It should be fixed after #23545 landed.",
- base_url.clone()
- );
- self.global().issue_page_warning(
- "Module scripts are not supported; ignoring inline module script.",
- );
- return;
- }
+ // Step 25-2.
+ match script_type {
+ ScriptType::Classic => {
+ if was_parser_inserted &&
+ doc.get_current_parser()
+ .map_or(false, |parser| parser.script_nesting_level() <= 1) &&
+ doc.get_script_blocking_stylesheets_count() > 0
+ {
+ // Step 26.h: classic, has no src, was parser-inserted, is blocked on stylesheet.
+ doc.set_pending_parsing_blocking_script(self, Some(result));
+ } else {
+ // Step 26.i: otherwise.
+ self.execute(result);
+ }
+ },
+ ScriptType::Module => {
+ // We should add inline module script elements
+ // into those vectors in case that there's no
+ // descendants in the inline module script.
+ if !r#async && was_parser_inserted {
+ doc.add_deferred_script(self);
+ } else if !r#async && !self.non_blocking.get() {
+ doc.push_asap_in_order_script(self);
+ } else {
+ doc.add_asap_script(self);
+ };
- // Step 26.
- if was_parser_inserted &&
- doc.get_current_parser()
- .map_or(false, |parser| parser.script_nesting_level() <= 1) &&
- doc.get_script_blocking_stylesheets_count() > 0
- {
- // Step 26.h: classic, has no src, was parser-inserted, is blocked on stylesheet.
- doc.set_pending_parsing_blocking_script(self, Some(result));
- } else {
- // Step 26.i: otherwise.
- self.execute(result);
+ fetch_inline_module_script(
+ ModuleOwner::Window(Trusted::new(self)),
+ text.clone(),
+ base_url.clone(),
+ self.id.clone(),
+ credentials_mode.unwrap(),
+ );
+ },
}
}
}
@@ -656,7 +712,7 @@ impl HTMLScriptElement {
}
/// <https://html.spec.whatwg.org/multipage/#execute-the-script-block>
- pub fn execute(&self, result: Result<ScriptOrigin, NetworkError>) {
+ pub fn execute(&self, result: ScriptResult) {
// Step 1.
let doc = document_from_node(self);
if self.parser_inserted.get() && &*doc != &*self.parser_document {
@@ -674,10 +730,12 @@ impl HTMLScriptElement {
Ok(script) => script,
};
- self.unminify_js(&mut script);
+ if script.type_ == ScriptType::Classic {
+ self.unminify_js(&mut script);
+ }
// Step 3.
- let neutralized_doc = if script.external {
+ let neutralized_doc = if script.external || script.type_ == ScriptType::Module {
debug!("loading external script, url = {}", script.url);
let doc = document_from_node(self);
doc.incr_ignore_destructive_writes_counter();
@@ -690,21 +748,24 @@ impl HTMLScriptElement {
let document = document_from_node(self);
let old_script = document.GetCurrentScript();
- // Step 5.a.1.
- document.set_current_script(Some(self));
-
- // Step 5.a.2.
- self.run_a_classic_script(&script);
-
- // Step 6.
- document.set_current_script(old_script.as_deref());
+ match script.type_ {
+ ScriptType::Classic => {
+ document.set_current_script(Some(self));
+ self.run_a_classic_script(&script);
+ document.set_current_script(old_script.as_deref());
+ },
+ ScriptType::Module => {
+ assert!(old_script.is_none());
+ self.run_a_module_script(&script, false);
+ },
+ }
- // Step 7.
+ // Step 5.
if let Some(doc) = neutralized_doc {
doc.decr_ignore_destructive_writes_counter();
}
- // Step 8.
+ // Step 6.
if script.external {
self.dispatch_load_event();
}
@@ -736,6 +797,72 @@ impl HTMLScriptElement {
);
}
+ #[allow(unsafe_code)]
+ /// https://html.spec.whatwg.org/multipage/#run-a-module-script
+ pub fn run_a_module_script(&self, script: &ScriptOrigin, _rethrow_errors: bool) {
+ // TODO use a settings object rather than this element's document/window
+ // Step 2
+ let document = document_from_node(self);
+ if !document.is_fully_active() || !document.is_scripting_enabled() {
+ return;
+ }
+
+ // Step 4
+ let window = window_from_node(self);
+ let global = window.upcast::<GlobalScope>();
+ let _aes = AutoEntryScript::new(&global);
+
+ if script.external {
+ let module_map = global.get_module_map().borrow();
+
+ if let Some(module_tree) = module_map.get(&script.url) {
+ // Step 6.
+ {
+ let module_error = module_tree.get_error().borrow();
+ if module_error.is_some() {
+ module_tree.report_error(&global);
+ return;
+ }
+ }
+
+ let module_record = module_tree.get_record().borrow();
+ if let Some(record) = &*module_record {
+ let evaluated = module_tree.execute_module(global, record.handle());
+
+ if let Err(exception) = evaluated {
+ module_tree.set_error(Some(exception.clone()));
+ module_tree.report_error(&global);
+ return;
+ }
+ }
+ }
+ } else {
+ let inline_module_map = global.get_inline_module_map().borrow();
+
+ if let Some(module_tree) = inline_module_map.get(&self.id.clone()) {
+ // Step 6.
+ {
+ let module_error = module_tree.get_error().borrow();
+ if module_error.is_some() {
+ module_tree.report_error(&global);
+ return;
+ }
+ }
+
+ let module_record = module_tree.get_record().borrow();
+ if let Some(record) = &*module_record {
+ let evaluated = module_tree.execute_module(global, record.handle());
+
+ if let Err(exception) = evaluated {
+ module_tree.set_error(Some(exception.clone()));
+ module_tree.report_error(&global);
+ return;
+ }
+ }
+ }
+ }
+ }
+
pub fn queue_error_event(&self) {
let window = window_from_node(self);
window
@@ -818,10 +945,18 @@ impl HTMLScriptElement {
self.parser_inserted.set(parser_inserted);
}
+ pub fn get_parser_inserted(&self) -> bool {
+ self.parser_inserted.get()
+ }
+
pub fn set_already_started(&self, already_started: bool) {
self.already_started.set(already_started);
}
+ pub fn get_non_blocking(&self) -> bool {
+ self.non_blocking.get()
+ }
+
fn dispatch_event(
&self,
type_: Atom,
@@ -930,6 +1065,11 @@ impl HTMLScriptElementMethods for HTMLScriptElement {
// https://html.spec.whatwg.org/multipage/#dom-script-defer
make_bool_setter!(SetDefer, "defer");
+ // https://html.spec.whatwg.org/multipage/#dom-script-nomodule
+ make_bool_getter!(NoModule, "nomodule");
+ // https://html.spec.whatwg.org/multipage/#dom-script-nomodule
+ make_bool_setter!(SetNoModule, "nomodule");
+
// https://html.spec.whatwg.org/multipage/#dom-script-integrity
make_getter!(Integrity, "integrity");
// https://html.spec.whatwg.org/multipage/#dom-script-integrity
diff --git a/components/script/dom/htmlselectelement.rs b/components/script/dom/htmlselectelement.rs
index c71a5442e73..0ef500ccb1a 100755
--- a/components/script/dom/htmlselectelement.rs
+++ b/components/script/dom/htmlselectelement.rs
@@ -61,6 +61,7 @@ pub struct HTMLSelectElement {
htmlelement: HTMLElement,
options: MutNullableDom<HTMLOptionsCollection>,
form_owner: MutNullableDom<HTMLFormElement>,
+ labels_node_list: MutNullableDom<NodeList>,
}
static DEFAULT_SELECT_SIZE: u32 = 0;
@@ -80,6 +81,7 @@ impl HTMLSelectElement {
),
options: Default::default(),
form_owner: Default::default(),
+ labels_node_list: Default::default(),
}
}
@@ -249,9 +251,7 @@ impl HTMLSelectElementMethods for HTMLSelectElement {
}
// https://html.spec.whatwg.org/multipage/#dom-lfe-labels
- fn Labels(&self) -> DomRoot<NodeList> {
- self.upcast::<HTMLElement>().labels()
- }
+ make_labels_getter!(Labels, labels_node_list);
// https://html.spec.whatwg.org/multipage/#dom-select-options
fn Options(&self) -> DomRoot<HTMLOptionsCollection> {
diff --git a/components/script/dom/htmltextareaelement.rs b/components/script/dom/htmltextareaelement.rs
index ed2372fb247..eb8fcab3873 100755
--- a/components/script/dom/htmltextareaelement.rs
+++ b/components/script/dom/htmltextareaelement.rs
@@ -52,6 +52,7 @@ pub struct HTMLTextAreaElement {
// https://html.spec.whatwg.org/multipage/#concept-textarea-dirty
value_dirty: Cell<bool>,
form_owner: MutNullableDom<HTMLFormElement>,
+ labels_node_list: MutNullableDom<NodeList>,
}
pub trait LayoutHTMLTextAreaElementHelpers {
@@ -153,6 +154,7 @@ impl HTMLTextAreaElement {
)),
value_dirty: Cell::new(false),
form_owner: Default::default(),
+ labels_node_list: Default::default(),
}
}
@@ -316,9 +318,7 @@ impl HTMLTextAreaElementMethods for HTMLTextAreaElement {
}
// https://html.spec.whatwg.org/multipage/#dom-lfe-labels
- fn Labels(&self) -> DomRoot<NodeList> {
- self.upcast::<HTMLElement>().labels()
- }
+ make_labels_getter!(Labels, labels_node_list);
// https://html.spec.whatwg.org/multipage/#dom-textarea/input-select
fn Select(&self) {
diff --git a/components/script/dom/htmltrackelement.rs b/components/script/dom/htmltrackelement.rs
index caa8932b415..c67701b3894 100644
--- a/components/script/dom/htmltrackelement.rs
+++ b/components/script/dom/htmltrackelement.rs
@@ -59,6 +59,7 @@ impl HTMLTrackElement {
Default::default(),
Default::default(),
Default::default(),
+ None,
);
Node::reflect_node(
Box::new(HTMLTrackElement::new_inherited(
diff --git a/components/script/dom/macros.rs b/components/script/dom/macros.rs
index a1941662963..fe578f6f675 100644
--- a/components/script/dom/macros.rs
+++ b/components/script/dom/macros.rs
@@ -141,6 +141,21 @@ macro_rules! make_form_action_getter(
);
#[macro_export]
+macro_rules! make_labels_getter(
+ ( $attr:ident, $memo:ident ) => (
+ fn $attr(&self) -> DomRoot<NodeList> {
+ use crate::dom::htmlelement::HTMLElement;
+ use crate::dom::nodelist::NodeList;
+ self.$memo.or_init(|| NodeList::new_labels_list(
+ self.upcast::<Node>().owner_doc().window(),
+ self.upcast::<HTMLElement>()
+ )
+ )
+ }
+ );
+);
+
+#[macro_export]
macro_rules! make_enumerated_getter(
( $attr:ident, $htmlname:tt, $default:expr, $($choices: pat)|+) => (
fn $attr(&self) -> DOMString {
diff --git a/components/script/dom/nodelist.rs b/components/script/dom/nodelist.rs
index 930fedbbec4..88fdd4cb70c 100644
--- a/components/script/dom/nodelist.rs
+++ b/components/script/dom/nodelist.rs
@@ -7,6 +7,7 @@ use crate::dom::bindings::codegen::Bindings::NodeListBinding;
use crate::dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods;
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
+use crate::dom::htmlelement::HTMLElement;
use crate::dom::node::{ChildrenMutation, Node};
use crate::dom::window::Window;
use dom_struct::dom_struct;
@@ -17,6 +18,7 @@ use std::cell::Cell;
pub enum NodeListType {
Simple(Vec<Dom<Node>>),
Children(ChildrenList),
+ Labels(LabelsList),
}
// https://dom.spec.whatwg.org/#interface-nodelist
@@ -65,6 +67,10 @@ impl NodeList {
NodeList::new(window, NodeListType::Children(ChildrenList::new(node)))
}
+ pub fn new_labels_list(window: &Window, element: &HTMLElement) -> DomRoot<NodeList> {
+ NodeList::new(window, NodeListType::Labels(LabelsList::new(element)))
+ }
+
pub fn empty(window: &Window) -> DomRoot<NodeList> {
NodeList::new(window, NodeListType::Simple(vec![]))
}
@@ -76,6 +82,7 @@ impl NodeListMethods for NodeList {
match self.list_type {
NodeListType::Simple(ref elems) => elems.len() as u32,
NodeListType::Children(ref list) => list.len(),
+ NodeListType::Labels(ref list) => list.len(),
}
}
@@ -86,6 +93,7 @@ impl NodeListMethods for NodeList {
.get(index as usize)
.map(|node| DomRoot::from_ref(&**node)),
NodeListType::Children(ref list) => list.item(index),
+ NodeListType::Labels(ref list) => list.item(index),
}
}
@@ -319,3 +327,33 @@ impl ChildrenList {
self.last_index.set(0u32);
}
}
+
+// Labels lists: There might room for performance optimization
+// analogous to the ChildrenMutation case of a children list,
+// in which we can keep information from an older access live
+// if we know nothing has happened that would change it.
+// However, label relationships can happen from further away
+// in the DOM than parent-child relationships, so it's not as simple,
+// and it's possible that tracking label moves would end up no faster
+// than recalculating labels.
+#[derive(JSTraceable, MallocSizeOf)]
+#[unrooted_must_root_lint::must_root]
+pub struct LabelsList {
+ element: Dom<HTMLElement>,
+}
+
+impl LabelsList {
+ pub fn new(element: &HTMLElement) -> LabelsList {
+ LabelsList {
+ element: Dom::from_ref(element),
+ }
+ }
+
+ pub fn len(&self) -> u32 {
+ self.element.labels_count()
+ }
+
+ pub fn item(&self, index: u32) -> Option<DomRoot<Node>> {
+ self.element.label_at(index)
+ }
+}
diff --git a/components/script/dom/promise.rs b/components/script/dom/promise.rs
index d2e209b98dd..785c1be8939 100644
--- a/components/script/dom/promise.rs
+++ b/components/script/dom/promise.rs
@@ -225,7 +225,7 @@ impl Promise {
}
#[allow(unsafe_code)]
- fn promise_obj(&self) -> HandleObject {
+ pub fn promise_obj(&self) -> HandleObject {
let obj = self.reflector().get_jsobject();
unsafe {
assert!(IsPromiseObject(obj));
diff --git a/components/script/dom/texttrack.rs b/components/script/dom/texttrack.rs
index 88c4859e05e..7c2b54adcd9 100644
--- a/components/script/dom/texttrack.rs
+++ b/components/script/dom/texttrack.rs
@@ -2,16 +2,18 @@
* 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 crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::TextTrackBinding::{
self, TextTrackKind, TextTrackMethods, TextTrackMode,
};
use crate::dom::bindings::error::{Error, ErrorResult};
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
-use crate::dom::bindings::root::{DomRoot, MutNullableDom};
+use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
use crate::dom::bindings::str::DOMString;
use crate::dom::eventtarget::EventTarget;
use crate::dom::texttrackcue::TextTrackCue;
use crate::dom::texttrackcuelist::TextTrackCueList;
+use crate::dom::texttracklist::TextTrackList;
use crate::dom::window::Window;
use dom_struct::dom_struct;
use std::cell::Cell;
@@ -25,6 +27,7 @@ pub struct TextTrack {
id: String,
mode: Cell<TextTrackMode>,
cue_list: MutNullableDom<TextTrackCueList>,
+ track_list: DomRefCell<Option<Dom<TextTrackList>>>,
}
impl TextTrack {
@@ -34,6 +37,7 @@ impl TextTrack {
label: DOMString,
language: DOMString,
mode: TextTrackMode,
+ track_list: Option<&TextTrackList>,
) -> TextTrack {
TextTrack {
eventtarget: EventTarget::new_inherited(),
@@ -43,6 +47,7 @@ impl TextTrack {
id: id.into(),
mode: Cell::new(mode),
cue_list: Default::default(),
+ track_list: DomRefCell::new(track_list.map(|t| Dom::from_ref(t))),
}
}
@@ -53,9 +58,12 @@ impl TextTrack {
label: DOMString,
language: DOMString,
mode: TextTrackMode,
+ track_list: Option<&TextTrackList>,
) -> DomRoot<TextTrack> {
reflect_dom_object(
- Box::new(TextTrack::new_inherited(id, kind, label, language, mode)),
+ Box::new(TextTrack::new_inherited(
+ id, kind, label, language, mode, track_list,
+ )),
window,
TextTrackBinding::Wrap,
)
@@ -69,6 +77,14 @@ impl TextTrack {
pub fn id(&self) -> &str {
&self.id
}
+
+ pub fn add_track_list(&self, track_list: &TextTrackList) {
+ *self.track_list.borrow_mut() = Some(Dom::from_ref(track_list));
+ }
+
+ pub fn remove_track_list(&self) {
+ *self.track_list.borrow_mut() = None;
+ }
}
impl TextTrackMethods for TextTrack {
diff --git a/components/script/dom/texttracklist.rs b/components/script/dom/texttracklist.rs
index f6f9037902e..0e978972d69 100644
--- a/components/script/dom/texttracklist.rs
+++ b/components/script/dom/texttracklist.rs
@@ -94,6 +94,7 @@ impl TextTrackList {
}),
&canceller,
);
+ track.add_track_list(self);
}
}
@@ -101,6 +102,9 @@ impl TextTrackList {
// removed from the TextTrackList.
#[allow(dead_code)]
pub fn remove(&self, idx: usize) {
+ if let Some(track) = self.dom_tracks.borrow().get(idx) {
+ track.remove_track_list();
+ }
self.dom_tracks.borrow_mut().remove(idx);
self.upcast::<EventTarget>()
.fire_event(atom!("removetrack"));
diff --git a/components/script/dom/videotrack.rs b/components/script/dom/videotrack.rs
index b1453da0df4..18a383ab816 100644
--- a/components/script/dom/videotrack.rs
+++ b/components/script/dom/videotrack.rs
@@ -2,6 +2,7 @@
* 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 crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::VideoTrackBinding::{self, VideoTrackMethods};
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot};
@@ -19,7 +20,7 @@ pub struct VideoTrack {
label: DOMString,
language: DOMString,
selected: Cell<bool>,
- track_list: Option<Dom<VideoTrackList>>,
+ track_list: DomRefCell<Option<Dom<VideoTrackList>>>,
}
impl VideoTrack {
@@ -37,7 +38,7 @@ impl VideoTrack {
label: label.into(),
language: language.into(),
selected: Cell::new(false),
- track_list: track_list.map(|t| Dom::from_ref(t)),
+ track_list: DomRefCell::new(track_list.map(|t| Dom::from_ref(t))),
}
}
@@ -73,6 +74,14 @@ impl VideoTrack {
pub fn set_selected(&self, value: bool) {
self.selected.set(value);
}
+
+ pub fn add_track_list(&self, track_list: &VideoTrackList) {
+ *self.track_list.borrow_mut() = Some(Dom::from_ref(track_list));
+ }
+
+ pub fn remove_track_list(&self) {
+ *self.track_list.borrow_mut() = None;
+ }
}
impl VideoTrackMethods for VideoTrack {
@@ -103,7 +112,7 @@ impl VideoTrackMethods for VideoTrack {
// https://html.spec.whatwg.org/multipage/#dom-videotrack-selected
fn SetSelected(&self, value: bool) {
- if let Some(list) = self.track_list.as_ref() {
+ if let Some(list) = self.track_list.borrow().as_ref() {
if let Some(idx) = list.find(self) {
list.set_selected(idx, value);
}
diff --git a/components/script/dom/videotracklist.rs b/components/script/dom/videotracklist.rs
index 9654123d3f3..98fa495bfdd 100644
--- a/components/script/dom/videotracklist.rs
+++ b/components/script/dom/videotracklist.rs
@@ -113,9 +113,14 @@ impl VideoTrackList {
self.set_selected(idx, false);
}
}
+ track.add_track_list(self);
}
pub fn clear(&self) {
+ self.tracks
+ .borrow()
+ .iter()
+ .for_each(|t| t.remove_track_list());
self.tracks.borrow_mut().clear();
}
}
diff --git a/components/script/dom/webgl_validations/tex_image_2d.rs b/components/script/dom/webgl_validations/tex_image_2d.rs
index fc5919132cf..54643e7a2da 100644
--- a/components/script/dom/webgl_validations/tex_image_2d.rs
+++ b/components/script/dom/webgl_validations/tex_image_2d.rs
@@ -47,10 +47,12 @@ pub enum TexImageValidationError {
InvalidOffsets,
}
-impl std::error::Error for TexImageValidationError {
- fn description(&self) -> &str {
+impl std::error::Error for TexImageValidationError {}
+
+impl fmt::Display for TexImageValidationError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::TexImageValidationError::*;
- match *self {
+ let description = match *self {
InvalidTextureTarget(_) => "Invalid texture target",
TextureTargetNotBound(_) => "Texture was not bound",
InvalidCubicTextureDimensions => {
@@ -68,17 +70,8 @@ impl std::error::Error for TexImageValidationError {
NonPotTexture => "Expected a power of two texture",
InvalidCompressionFormat => "Unrecognized texture compression format",
InvalidOffsets => "Invalid X/Y texture offset parameters",
- }
- }
-}
-
-impl fmt::Display for TexImageValidationError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(
- f,
- "TexImageValidationError({})",
- std::error::Error::description(self)
- )
+ };
+ write!(f, "TexImageValidationError({})", description)
}
}
diff --git a/components/script/dom/webidls/CustomElementRegistry.webidl b/components/script/dom/webidls/CustomElementRegistry.webidl
index 6d3a7b436de..3ba49a2082c 100644
--- a/components/script/dom/webidls/CustomElementRegistry.webidl
+++ b/components/script/dom/webidls/CustomElementRegistry.webidl
@@ -11,6 +11,8 @@ interface CustomElementRegistry {
any get(DOMString name);
Promise<void> whenDefined(DOMString name);
+
+ [CEReactions] void upgrade(Node root);
};
callback CustomElementConstructor = HTMLElement();
diff --git a/components/script/dom/webidls/Document.webidl b/components/script/dom/webidls/Document.webidl
index 9476b9c2d6d..7d92ddbd137 100644
--- a/components/script/dom/webidls/Document.webidl
+++ b/components/script/dom/webidls/Document.webidl
@@ -34,9 +34,10 @@ interface Document : Node {
HTMLCollection getElementsByClassName(DOMString classNames);
[CEReactions, NewObject, Throws]
- Element createElement(DOMString localName, optional ElementCreationOptions options = {});
+ Element createElement(DOMString localName, optional (DOMString or ElementCreationOptions) options = {});
[CEReactions, NewObject, Throws]
- Element createElementNS(DOMString? namespace, DOMString qualifiedName, optional ElementCreationOptions options = {});
+ Element createElementNS(DOMString? namespace, DOMString qualifiedName,
+ optional (DOMString or ElementCreationOptions) options = {});
[NewObject]
DocumentFragment createDocumentFragment();
[NewObject]
diff --git a/components/script/dom/webidls/HTMLInputElement.webidl b/components/script/dom/webidls/HTMLInputElement.webidl
index 48001bc555c..64ee9aa7c2f 100644
--- a/components/script/dom/webidls/HTMLInputElement.webidl
+++ b/components/script/dom/webidls/HTMLInputElement.webidl
@@ -89,7 +89,7 @@ interface HTMLInputElement : HTMLElement {
//boolean reportValidity();
//void setCustomValidity(DOMString error);
- readonly attribute NodeList labels;
+ readonly attribute NodeList? labels;
void select();
[SetterThrows]
diff --git a/components/script/dom/webidls/HTMLOptionElement.webidl b/components/script/dom/webidls/HTMLOptionElement.webidl
index 56a379fc8db..65f37458295 100644
--- a/components/script/dom/webidls/HTMLOptionElement.webidl
+++ b/components/script/dom/webidls/HTMLOptionElement.webidl
@@ -3,9 +3,9 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
// https://html.spec.whatwg.org/multipage/#htmloptionelement
-[Exposed=Window/*, NamedConstructor=Option(optional DOMString text = "", optional DOMString value,
+[Exposed=Window, NamedConstructor=Option(optional DOMString text = "", optional DOMString value,
optional boolean defaultSelected = false,
- optional boolean selected = false)*/]
+ optional boolean selected = false)]
interface HTMLOptionElement : HTMLElement {
[HTMLConstructor] constructor();
diff --git a/components/script/dom/webidls/HTMLScriptElement.webidl b/components/script/dom/webidls/HTMLScriptElement.webidl
index f7126b7901b..13b69865fa5 100644
--- a/components/script/dom/webidls/HTMLScriptElement.webidl
+++ b/components/script/dom/webidls/HTMLScriptElement.webidl
@@ -12,6 +12,8 @@ interface HTMLScriptElement : HTMLElement {
[CEReactions]
attribute DOMString type;
[CEReactions]
+ attribute boolean noModule;
+ [CEReactions]
attribute DOMString charset;
[CEReactions]
attribute boolean async;
diff --git a/components/script/dom/worker.rs b/components/script/dom/worker.rs
index 2d126933849..7eb958cd095 100644
--- a/components/script/dom/worker.rs
+++ b/components/script/dom/worker.rs
@@ -140,11 +140,7 @@ impl Worker {
self.terminated.get()
}
- pub fn handle_message(
- address: TrustedWorkerAddress,
- origin: String,
- data: StructuredSerializedData,
- ) {
+ pub fn handle_message(address: TrustedWorkerAddress, data: StructuredSerializedData) {
let worker = address.root();
if worker.is_terminated() {
@@ -156,14 +152,7 @@ impl Worker {
let _ac = enter_realm(target);
rooted!(in(*global.get_cx()) let mut message = UndefinedValue());
if let Ok(ports) = structuredclone::read(&global, data, message.handle_mut()) {
- MessageEvent::dispatch_jsval(
- target,
- &global,
- message.handle(),
- Some(&origin),
- None,
- ports,
- );
+ MessageEvent::dispatch_jsval(target, &global, message.handle(), None, None, ports);
} else {
// Step 4 of the "port post message steps" of the implicit messageport, fire messageerror.
MessageEvent::dispatch_error(target, &global);
diff --git a/components/script/dom/xr.rs b/components/script/dom/xr.rs
index 72cc573d645..5804918a532 100644
--- a/components/script/dom/xr.rs
+++ b/components/script/dom/xr.rs
@@ -26,14 +26,14 @@ use crate::dom::xrtest::XRTest;
use crate::script_thread::ScriptThread;
use crate::task_source::TaskSource;
use dom_struct::dom_struct;
-use ipc_channel::ipc::IpcSender;
+use ipc_channel::ipc::{self as ipc_crate, IpcReceiver, IpcSender};
use ipc_channel::router::ROUTER;
use profile_traits::ipc;
use std::cell::Cell;
use std::rc::Rc;
use webvr_traits::{WebVRDisplayData, WebVRDisplayEvent, WebVREvent, WebVRMsg};
use webvr_traits::{WebVRGamepadData, WebVRGamepadEvent, WebVRGamepadState};
-use webxr_api::{Error as XRError, Session, SessionMode};
+use webxr_api::{Error as XRError, Frame, Session, SessionMode};
#[dom_struct]
pub struct XR {
@@ -185,12 +185,15 @@ impl XRMethods for XR {
.task_manager()
.dom_manipulation_task_source_with_canceller();
let (sender, receiver) = ipc::channel(global.time_profiler_chan().clone()).unwrap();
+ let (frame_sender, frame_receiver) = ipc_crate::channel().unwrap();
+ let mut frame_receiver = Some(frame_receiver);
ROUTER.add_route(
receiver.to_opaque(),
Box::new(move |message| {
// router doesn't know this is only called once
let trusted = trusted.take().unwrap();
let this = this.clone();
+ let frame_receiver = frame_receiver.take().unwrap();
let message: Result<Session, webxr_api::Error> = if let Ok(message) = message.to() {
message
} else {
@@ -199,13 +202,15 @@ impl XRMethods for XR {
};
let _ = task_source.queue_with_canceller(
task!(request_session: move || {
- this.root().session_obtained(message, trusted.root(), mode);
+ this.root().session_obtained(message, trusted.root(), mode, frame_receiver);
}),
&canceller,
);
}),
);
- window.webxr_registry().request_session(mode.into(), sender);
+ window
+ .webxr_registry()
+ .request_session(mode.into(), sender, frame_sender);
promise
}
@@ -222,6 +227,7 @@ impl XR {
response: Result<Session, XRError>,
promise: Rc<Promise>,
mode: XRSessionMode,
+ frame_receiver: IpcReceiver<Frame>,
) {
let session = match response {
Ok(session) => session,
@@ -231,7 +237,7 @@ impl XR {
},
};
- let session = XRSession::new(&self.global(), session, mode);
+ let session = XRSession::new(&self.global(), session, mode, frame_receiver);
if mode == XRSessionMode::Inline {
self.active_inline_sessions
.borrow_mut()
diff --git a/components/script/dom/xrsession.rs b/components/script/dom/xrsession.rs
index 6e076b6daba..a4ced8689a5 100644
--- a/components/script/dom/xrsession.rs
+++ b/components/script/dom/xrsession.rs
@@ -41,7 +41,7 @@ use crate::dom::xrwebgllayer::XRWebGLLayer;
use crate::task_source::TaskSource;
use dom_struct::dom_struct;
use euclid::{Rect, RigidTransform3D, Transform3D};
-use ipc_channel::ipc::IpcSender;
+use ipc_channel::ipc::IpcReceiver;
use ipc_channel::router::ROUTER;
use metrics::ToMs;
use profile_traits::ipc;
@@ -73,8 +73,6 @@ pub struct XRSession {
next_raf_id: Cell<i32>,
#[ignore_malloc_size_of = "closures are hard"]
raf_callback_list: DomRefCell<Vec<(i32, Option<Rc<XRFrameRequestCallback>>)>>,
- #[ignore_malloc_size_of = "defined in ipc-channel"]
- raf_sender: DomRefCell<Option<IpcSender<Frame>>>,
input_sources: Dom<XRInputSourceArray>,
// Any promises from calling end()
#[ignore_malloc_size_of = "promises are hard"]
@@ -108,7 +106,6 @@ impl XRSession {
next_raf_id: Cell::new(0),
raf_callback_list: DomRefCell::new(vec![]),
- raf_sender: DomRefCell::new(None),
input_sources: Dom::from_ref(input_sources),
end_promises: DomRefCell::new(vec![]),
ended: Cell::new(false),
@@ -116,7 +113,12 @@ impl XRSession {
}
}
- pub fn new(global: &GlobalScope, session: Session, mode: XRSessionMode) -> DomRoot<XRSession> {
+ pub fn new(
+ global: &GlobalScope,
+ session: Session,
+ mode: XRSessionMode,
+ frame_receiver: IpcReceiver<Frame>,
+ ) -> DomRoot<XRSession> {
let ivfov = if mode == XRSessionMode::Inline {
Some(FRAC_PI_2)
} else {
@@ -136,7 +138,7 @@ impl XRSession {
);
input_sources.set_initial_inputs(&ret);
ret.attach_event_handler();
- ret.setup_raf_loop();
+ ret.setup_raf_loop(frame_receiver);
ret
}
@@ -153,21 +155,15 @@ impl XRSession {
self.mode != XRSessionMode::Inline
}
- fn setup_raf_loop(&self) {
- assert!(
- self.raf_sender.borrow().is_none(),
- "RAF loop already set up"
- );
+ fn setup_raf_loop(&self, frame_receiver: IpcReceiver<Frame>) {
let this = Trusted::new(self);
let global = self.global();
let window = global.as_window();
let (task_source, canceller) = window
.task_manager()
.dom_manipulation_task_source_with_canceller();
- let (sender, receiver) = ipc::channel(global.time_profiler_chan().clone()).unwrap();
- *self.raf_sender.borrow_mut() = Some(sender);
ROUTER.add_route(
- receiver.to_opaque(),
+ frame_receiver.to_opaque(),
Box::new(move |message| {
let this = this.clone();
let _ = task_source.queue_with_canceller(
@@ -179,15 +175,7 @@ impl XRSession {
}),
);
- self.request_new_xr_frame();
- }
-
- /// Requests a new https://immersive-web.github.io/webxr/#xr-animation-frame
- ///
- /// This happens regardless of the presense of rAF callbacks
- fn request_new_xr_frame(&self) {
- let sender = self.raf_sender.borrow().clone().unwrap();
- self.session.borrow_mut().request_animation_frame(sender);
+ self.session.borrow_mut().start_render_loop();
}
pub fn is_outside_raf(&self) -> bool {
@@ -360,8 +348,9 @@ impl XRSession {
if self.is_immersive() {
base_layer.swap_buffers();
self.session.borrow_mut().render_animation_frame();
+ } else {
+ self.session.borrow_mut().start_render_loop();
}
- self.request_new_xr_frame();
// If the canvas element is attached to the DOM, it is now dirty,
// and we need to trigger a reflow.
diff --git a/components/script/lib.rs b/components/script/lib.rs
index 79c0657b996..2ae7f76923a 100644
--- a/components/script/lib.rs
+++ b/components/script/lib.rs
@@ -83,6 +83,8 @@ mod microtask;
#[warn(deprecated)]
mod network_listener;
#[warn(deprecated)]
+mod script_module;
+#[warn(deprecated)]
pub mod script_runtime;
#[warn(deprecated)]
#[allow(unsafe_code)]
diff --git a/components/script/script_module.rs b/components/script/script_module.rs
new file mode 100644
index 00000000000..4bb60f5a233
--- /dev/null
+++ b/components/script/script_module.rs
@@ -0,0 +1,1430 @@
+/* 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/. */
+
+//! The script module mod contains common traits and structs
+//! related to `type=module` for script thread or worker threads.
+
+use crate::compartments::{enter_realm, AlreadyInCompartment, InCompartment};
+use crate::document_loader::LoadType;
+use crate::dom::bindings::cell::DomRefCell;
+use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods;
+use crate::dom::bindings::conversions::jsstring_to_str;
+use crate::dom::bindings::error::report_pending_exception;
+use crate::dom::bindings::error::Error;
+use crate::dom::bindings::inheritance::Castable;
+use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
+use crate::dom::bindings::reflector::DomObject;
+use crate::dom::bindings::root::DomRoot;
+use crate::dom::bindings::settings_stack::AutoIncumbentScript;
+use crate::dom::bindings::str::DOMString;
+use crate::dom::bindings::trace::RootedTraceableBox;
+use crate::dom::document::Document;
+use crate::dom::element::Element;
+use crate::dom::globalscope::GlobalScope;
+use crate::dom::htmlscriptelement::{HTMLScriptElement, ScriptId};
+use crate::dom::htmlscriptelement::{ScriptOrigin, ScriptType, SCRIPT_JS_MIMES};
+use crate::dom::node::document_from_node;
+use crate::dom::performanceresourcetiming::InitiatorType;
+use crate::dom::promise::Promise;
+use crate::dom::promisenativehandler::{Callback, PromiseNativeHandler};
+use crate::dom::window::Window;
+use crate::dom::worker::TrustedWorkerAddress;
+use crate::network_listener::{self, NetworkListener};
+use crate::network_listener::{PreInvoke, ResourceTimingListener};
+use crate::task::TaskBox;
+use crate::task_source::TaskSourceName;
+use encoding_rs::UTF_8;
+use hyper_serde::Serde;
+use ipc_channel::ipc;
+use ipc_channel::router::ROUTER;
+use js::glue::{AppendToAutoObjectVector, CreateAutoObjectVector};
+use js::jsapi::Handle as RawHandle;
+use js::jsapi::HandleObject;
+use js::jsapi::HandleValue as RawHandleValue;
+use js::jsapi::{AutoObjectVector, JSAutoRealm, JSObject, JSString};
+use js::jsapi::{GetModuleResolveHook, JSRuntime, SetModuleResolveHook};
+use js::jsapi::{GetRequestedModules, SetModuleMetadataHook};
+use js::jsapi::{GetWaitForAllPromise, ModuleEvaluate, ModuleInstantiate, SourceText};
+use js::jsapi::{Heap, JSContext, JS_ClearPendingException, SetModulePrivate};
+use js::jsapi::{SetModuleDynamicImportHook, SetScriptPrivateReferenceHooks};
+use js::jsval::{JSVal, PrivateValue, UndefinedValue};
+use js::rust::jsapi_wrapped::{CompileModule, JS_GetArrayLength, JS_GetElement};
+use js::rust::jsapi_wrapped::{GetRequestedModuleSpecifier, JS_GetPendingException};
+use js::rust::wrappers::JS_SetPendingException;
+use js::rust::CompileOptionsWrapper;
+use js::rust::IntoHandle;
+use js::rust::{Handle, HandleValue};
+use net_traits::request::{CredentialsMode, Destination, ParserMetadata};
+use net_traits::request::{Referrer, RequestBuilder, RequestMode};
+use net_traits::{FetchMetadata, Metadata};
+use net_traits::{FetchResponseListener, NetworkError};
+use net_traits::{ResourceFetchTiming, ResourceTimingType};
+use servo_url::ServoUrl;
+use std::cmp::Ordering;
+use std::collections::{HashMap, HashSet};
+use std::ffi;
+use std::marker::PhantomData;
+use std::ptr;
+use std::rc::Rc;
+use std::sync::{Arc, Mutex};
+use url::ParseError as UrlParseError;
+
+use indexmap::IndexSet;
+
+pub fn get_source_text(source: &[u16]) -> SourceText<u16> {
+ SourceText {
+ units_: source.as_ptr() as *const _,
+ length_: source.len() as u32,
+ ownsUnits_: false,
+ _phantom_0: PhantomData,
+ }
+}
+
+#[allow(unsafe_code)]
+unsafe fn gen_type_error(global: &GlobalScope, string: String) -> ModuleError {
+ rooted!(in(*global.get_cx()) let mut thrown = UndefinedValue());
+ Error::Type(string).to_jsval(*global.get_cx(), &global, thrown.handle_mut());
+
+ return ModuleError::RawException(RootedTraceableBox::from_box(Heap::boxed(thrown.get())));
+}
+
+#[derive(JSTraceable)]
+pub struct ModuleObject(Box<Heap<*mut JSObject>>);
+
+impl ModuleObject {
+ #[allow(unsafe_code)]
+ pub fn handle(&self) -> HandleObject {
+ unsafe { self.0.handle() }
+ }
+}
+
+#[derive(JSTraceable)]
+pub enum ModuleError {
+ Network(NetworkError),
+ RawException(RootedTraceableBox<Heap<JSVal>>),
+}
+
+impl Eq for ModuleError {}
+
+impl PartialEq for ModuleError {
+ fn eq(&self, other: &Self) -> bool {
+ match (self, other) {
+ (Self::Network(_), Self::RawException(_)) |
+ (Self::RawException(_), Self::Network(_)) => false,
+ _ => true,
+ }
+ }
+}
+
+impl Ord for ModuleError {
+ fn cmp(&self, other: &Self) -> Ordering {
+ match (self, other) {
+ (Self::Network(_), Self::RawException(_)) => Ordering::Greater,
+ (Self::RawException(_), Self::Network(_)) => Ordering::Less,
+ _ => Ordering::Equal,
+ }
+ }
+}
+
+impl PartialOrd for ModuleError {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl ModuleError {
+ #[allow(unsafe_code)]
+ pub fn handle(&self) -> Handle<JSVal> {
+ match self {
+ Self::Network(_) => unreachable!(),
+ Self::RawException(exception) => exception.handle(),
+ }
+ }
+}
+
+impl Clone for ModuleError {
+ fn clone(&self) -> Self {
+ match self {
+ Self::Network(network_error) => Self::Network(network_error.clone()),
+ Self::RawException(exception) => Self::RawException(RootedTraceableBox::from_box(
+ Heap::boxed(exception.get().clone()),
+ )),
+ }
+ }
+}
+
+struct ModuleScript {
+ base_url: ServoUrl,
+}
+
+#[derive(JSTraceable)]
+pub struct ModuleTree {
+ url: ServoUrl,
+ text: DomRefCell<DOMString>,
+ record: DomRefCell<Option<ModuleObject>>,
+ status: DomRefCell<ModuleStatus>,
+ // The spec maintains load order for descendants, so we use an indexset for descendants and
+ // parents. This isn't actually necessary for parents however the IndexSet APIs don't
+ // interop with HashSet, and IndexSet isn't very expensive
+ // (https://github.com/bluss/indexmap/issues/110)
+ //
+ // By default all maps in web specs are ordered maps
+ // (https://infra.spec.whatwg.org/#ordered-map), however we can usually get away with using
+ // stdlib maps and sets because we rarely iterate over them.
+ parent_urls: DomRefCell<IndexSet<ServoUrl>>,
+ descendant_urls: DomRefCell<IndexSet<ServoUrl>>,
+ visited_urls: DomRefCell<HashSet<ServoUrl>>,
+ error: DomRefCell<Option<ModuleError>>,
+ promise: DomRefCell<Option<Rc<Promise>>>,
+}
+
+impl ModuleTree {
+ pub fn new(url: ServoUrl) -> Self {
+ ModuleTree {
+ url,
+ text: DomRefCell::new(DOMString::new()),
+ record: DomRefCell::new(None),
+ status: DomRefCell::new(ModuleStatus::Initial),
+ parent_urls: DomRefCell::new(IndexSet::new()),
+ descendant_urls: DomRefCell::new(IndexSet::new()),
+ visited_urls: DomRefCell::new(HashSet::new()),
+ error: DomRefCell::new(None),
+ promise: DomRefCell::new(None),
+ }
+ }
+
+ pub fn get_promise(&self) -> &DomRefCell<Option<Rc<Promise>>> {
+ &self.promise
+ }
+
+ pub fn set_promise(&self, promise: Rc<Promise>) {
+ *self.promise.borrow_mut() = Some(promise);
+ }
+
+ pub fn get_status(&self) -> ModuleStatus {
+ self.status.borrow().clone()
+ }
+
+ pub fn set_status(&self, status: ModuleStatus) {
+ *self.status.borrow_mut() = status;
+ }
+
+ pub fn get_record(&self) -> &DomRefCell<Option<ModuleObject>> {
+ &self.record
+ }
+
+ pub fn set_record(&self, record: ModuleObject) {
+ *self.record.borrow_mut() = Some(record);
+ }
+
+ pub fn get_error(&self) -> &DomRefCell<Option<ModuleError>> {
+ &self.error
+ }
+
+ pub fn set_error(&self, error: Option<ModuleError>) {
+ *self.error.borrow_mut() = error;
+ }
+
+ pub fn get_text(&self) -> &DomRefCell<DOMString> {
+ &self.text
+ }
+
+ pub fn set_text(&self, module_text: DOMString) {
+ *self.text.borrow_mut() = module_text;
+ }
+
+ pub fn get_parent_urls(&self) -> &DomRefCell<IndexSet<ServoUrl>> {
+ &self.parent_urls
+ }
+
+ pub fn insert_parent_url(&self, parent_url: ServoUrl) {
+ self.parent_urls.borrow_mut().insert(parent_url);
+ }
+
+ pub fn append_parent_urls(&self, parent_urls: IndexSet<ServoUrl>) {
+ self.parent_urls.borrow_mut().extend(parent_urls);
+ }
+
+ pub fn get_descendant_urls(&self) -> &DomRefCell<IndexSet<ServoUrl>> {
+ &self.descendant_urls
+ }
+
+ pub fn append_descendant_urls(&self, descendant_urls: IndexSet<ServoUrl>) {
+ self.descendant_urls.borrow_mut().extend(descendant_urls);
+ }
+
+ /// recursively checks if all of the transitive descendants are
+ /// in the FetchingDescendants or later status
+ fn recursive_check_descendants(
+ module_tree: &ModuleTree,
+ module_map: &HashMap<ServoUrl, Rc<ModuleTree>>,
+ discovered_urls: &mut HashSet<ServoUrl>,
+ ) -> bool {
+ discovered_urls.insert(module_tree.url.clone());
+
+ let descendant_urls = module_tree.descendant_urls.borrow();
+
+ for descendant_module in descendant_urls
+ .iter()
+ .filter_map(|url| module_map.get(&url.clone()))
+ {
+ if discovered_urls.contains(&descendant_module.url) {
+ continue;
+ }
+
+ let descendant_status = descendant_module.get_status();
+ if descendant_status < ModuleStatus::FetchingDescendants {
+ return false;
+ }
+
+ let all_ready_descendants = ModuleTree::recursive_check_descendants(
+ &descendant_module,
+ module_map,
+ discovered_urls,
+ );
+
+ if !all_ready_descendants {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ fn has_all_ready_descendants(&self, module_map: &HashMap<ServoUrl, Rc<ModuleTree>>) -> bool {
+ let mut discovered_urls = HashSet::new();
+
+ return ModuleTree::recursive_check_descendants(&self, module_map, &mut discovered_urls);
+ }
+
+ pub fn get_visited_urls(&self) -> &DomRefCell<HashSet<ServoUrl>> {
+ &self.visited_urls
+ }
+
+ pub fn append_handler(&self, owner: ModuleOwner, module_url: ServoUrl, is_top_level: bool) {
+ let promise = self.promise.borrow();
+
+ let resolve_this = owner.clone();
+ let reject_this = owner.clone();
+
+ let resolved_url = module_url.clone();
+ let rejected_url = module_url.clone();
+
+ let handler = PromiseNativeHandler::new(
+ &owner.global(),
+ Some(ModuleHandler::new(Box::new(
+ task!(fetched_resolve: move || {
+ resolve_this.finish_module_load(Some(resolved_url), is_top_level);
+ }),
+ ))),
+ Some(ModuleHandler::new(Box::new(
+ task!(failure_reject: move || {
+ reject_this.finish_module_load(Some(rejected_url), is_top_level);
+ }),
+ ))),
+ );
+
+ let _compartment = enter_realm(&*owner.global());
+ AlreadyInCompartment::assert(&*owner.global());
+ let _ais = AutoIncumbentScript::new(&*owner.global());
+
+ let promise = promise.as_ref().unwrap();
+
+ promise.append_native_handler(&handler);
+ }
+}
+
+#[derive(Clone, Copy, Debug, JSTraceable, PartialEq, PartialOrd)]
+pub enum ModuleStatus {
+ Initial,
+ Fetching,
+ FetchingDescendants,
+ FetchFailed,
+ Ready,
+ Finished,
+}
+
+impl ModuleTree {
+ #[allow(unsafe_code)]
+ /// https://html.spec.whatwg.org/multipage/#creating-a-module-script
+ /// Step 7-11.
+ fn compile_module_script(
+ &self,
+ global: &GlobalScope,
+ module_script_text: DOMString,
+ url: ServoUrl,
+ ) -> Result<ModuleObject, ModuleError> {
+ let module: Vec<u16> = module_script_text.encode_utf16().collect();
+
+ let url_cstr = ffi::CString::new(url.as_str().as_bytes()).unwrap();
+
+ let _ac = JSAutoRealm::new(*global.get_cx(), *global.reflector().get_jsobject());
+
+ let compile_options = CompileOptionsWrapper::new(*global.get_cx(), url_cstr.as_ptr(), 1);
+
+ rooted!(in(*global.get_cx()) let mut module_script = ptr::null_mut::<JSObject>());
+
+ let mut source = get_source_text(&module);
+
+ unsafe {
+ if !CompileModule(
+ *global.get_cx(),
+ compile_options.ptr,
+ &mut source,
+ &mut module_script.handle_mut(),
+ ) {
+ warn!("fail to compile module script of {}", url);
+
+ rooted!(in(*global.get_cx()) let mut exception = UndefinedValue());
+ assert!(JS_GetPendingException(
+ *global.get_cx(),
+ &mut exception.handle_mut()
+ ));
+ JS_ClearPendingException(*global.get_cx());
+
+ return Err(ModuleError::RawException(RootedTraceableBox::from_box(
+ Heap::boxed(exception.get()),
+ )));
+ }
+
+ let module_script_data = Box::new(ModuleScript {
+ base_url: url.clone(),
+ });
+
+ SetModulePrivate(
+ module_script.get(),
+ &PrivateValue(Box::into_raw(module_script_data) as *const _),
+ );
+ }
+
+ debug!("module script of {} compile done", url);
+
+ self.resolve_requested_module_specifiers(
+ &global,
+ module_script.handle().into_handle(),
+ url.clone(),
+ )
+ .map(|_| ModuleObject(Heap::boxed(*module_script)))
+ }
+
+ #[allow(unsafe_code)]
+ pub fn instantiate_module_tree(
+ &self,
+ global: &GlobalScope,
+ module_record: HandleObject,
+ ) -> Result<(), ModuleError> {
+ let _ac = JSAutoRealm::new(*global.get_cx(), *global.reflector().get_jsobject());
+
+ unsafe {
+ if !ModuleInstantiate(*global.get_cx(), module_record) {
+ warn!("fail to instantiate module");
+
+ rooted!(in(*global.get_cx()) let mut exception = UndefinedValue());
+ assert!(JS_GetPendingException(
+ *global.get_cx(),
+ &mut exception.handle_mut()
+ ));
+ JS_ClearPendingException(*global.get_cx());
+
+ Err(ModuleError::RawException(RootedTraceableBox::from_box(
+ Heap::boxed(exception.get()),
+ )))
+ } else {
+ debug!("module instantiated successfully");
+
+ Ok(())
+ }
+ }
+ }
+
+ #[allow(unsafe_code)]
+ pub fn execute_module(
+ &self,
+ global: &GlobalScope,
+ module_record: HandleObject,
+ ) -> Result<(), ModuleError> {
+ let _ac = JSAutoRealm::new(*global.get_cx(), *global.reflector().get_jsobject());
+
+ unsafe {
+ if !ModuleEvaluate(*global.get_cx(), module_record) {
+ warn!("fail to evaluate module");
+
+ rooted!(in(*global.get_cx()) let mut exception = UndefinedValue());
+ assert!(JS_GetPendingException(
+ *global.get_cx(),
+ &mut exception.handle_mut()
+ ));
+ JS_ClearPendingException(*global.get_cx());
+
+ Err(ModuleError::RawException(RootedTraceableBox::from_box(
+ Heap::boxed(exception.get()),
+ )))
+ } else {
+ debug!("module evaluated successfully");
+ Ok(())
+ }
+ }
+ }
+
+ #[allow(unsafe_code)]
+ pub fn report_error(&self, global: &GlobalScope) {
+ let module_error = self.error.borrow();
+
+ if let Some(exception) = &*module_error {
+ unsafe {
+ JS_SetPendingException(*global.get_cx(), exception.handle());
+ report_pending_exception(*global.get_cx(), true);
+ }
+ }
+ }
+
+ /// https://html.spec.whatwg.org/multipage/#fetch-the-descendants-of-a-module-script
+ /// Step 5.
+ pub fn resolve_requested_modules(
+ &self,
+ global: &GlobalScope,
+ ) -> Result<IndexSet<ServoUrl>, ModuleError> {
+ let status = self.get_status();
+
+ assert_ne!(status, ModuleStatus::Initial);
+ assert_ne!(status, ModuleStatus::Fetching);
+
+ let record = self.record.borrow();
+
+ if let Some(raw_record) = &*record {
+ let valid_specifier_urls = self.resolve_requested_module_specifiers(
+ &global,
+ raw_record.handle(),
+ self.url.clone(),
+ );
+
+ return valid_specifier_urls.map(|parsed_urls| {
+ parsed_urls
+ .iter()
+ .filter_map(|parsed_url| {
+ let mut visited = self.visited_urls.borrow_mut();
+
+ if !visited.contains(&parsed_url) {
+ visited.insert(parsed_url.clone());
+
+ Some(parsed_url.clone())
+ } else {
+ None
+ }
+ })
+ .collect::<IndexSet<ServoUrl>>()
+ });
+ }
+
+ unreachable!("Didn't have record while resolving its requested module")
+ }
+
+ #[allow(unsafe_code)]
+ fn resolve_requested_module_specifiers(
+ &self,
+ global: &GlobalScope,
+ module_object: HandleObject,
+ base_url: ServoUrl,
+ ) -> Result<IndexSet<ServoUrl>, ModuleError> {
+ let _ac = JSAutoRealm::new(*global.get_cx(), *global.reflector().get_jsobject());
+
+ let mut specifier_urls = IndexSet::new();
+
+ unsafe {
+ rooted!(in(*global.get_cx()) let requested_modules = GetRequestedModules(*global.get_cx(), module_object));
+
+ let mut length = 0;
+
+ if !JS_GetArrayLength(*global.get_cx(), requested_modules.handle(), &mut length) {
+ let module_length_error =
+ gen_type_error(&global, "Wrong length of requested modules".to_owned());
+
+ return Err(module_length_error);
+ }
+
+ for index in 0..length {
+ rooted!(in(*global.get_cx()) let mut element = UndefinedValue());
+
+ if !JS_GetElement(
+ *global.get_cx(),
+ requested_modules.handle(),
+ index,
+ &mut element.handle_mut(),
+ ) {
+ let get_element_error =
+ gen_type_error(&global, "Failed to get requested module".to_owned());
+
+ return Err(get_element_error);
+ }
+
+ rooted!(in(*global.get_cx()) let specifier = GetRequestedModuleSpecifier(
+ *global.get_cx(), element.handle()
+ ));
+
+ let url = ModuleTree::resolve_module_specifier(
+ *global.get_cx(),
+ &base_url,
+ specifier.handle().into_handle(),
+ );
+
+ if url.is_err() {
+ let specifier_error =
+ gen_type_error(&global, "Wrong module specifier".to_owned());
+
+ return Err(specifier_error);
+ }
+
+ specifier_urls.insert(url.unwrap());
+ }
+ }
+
+ Ok(specifier_urls)
+ }
+
+ /// The following module specifiers are allowed by the spec:
+ /// - a valid absolute URL
+ /// - a valid relative URL that starts with "/", "./" or "../"
+ ///
+ /// Bareword module specifiers are currently disallowed as these may be given
+ /// special meanings in the future.
+ /// https://html.spec.whatwg.org/multipage/#resolve-a-module-specifier
+ #[allow(unsafe_code)]
+ fn resolve_module_specifier(
+ cx: *mut JSContext,
+ url: &ServoUrl,
+ specifier: RawHandle<*mut JSString>,
+ ) -> Result<ServoUrl, UrlParseError> {
+ let specifier_str = unsafe { jsstring_to_str(cx, *specifier) };
+
+ // Step 1.
+ if let Ok(specifier_url) = ServoUrl::parse(&specifier_str) {
+ return Ok(specifier_url);
+ }
+
+ // Step 2.
+ if !specifier_str.starts_with("/") &&
+ !specifier_str.starts_with("./") &&
+ !specifier_str.starts_with("../")
+ {
+ return Err(UrlParseError::InvalidDomainCharacter);
+ }
+
+ // Step 3.
+ return ServoUrl::parse_with_base(Some(url), &specifier_str.clone());
+ }
+
+ /// https://html.spec.whatwg.org/multipage/#finding-the-first-parse-error
+ fn find_first_parse_error(
+ global: &GlobalScope,
+ module_tree: &ModuleTree,
+ discovered_urls: &mut HashSet<ServoUrl>,
+ ) -> Option<ModuleError> {
+ // 3.
+ discovered_urls.insert(module_tree.url.clone());
+
+ // 4.
+ let module_map = global.get_module_map().borrow();
+ let record = module_tree.get_record().borrow();
+ if record.is_none() {
+ let module_error = module_tree.get_error().borrow();
+
+ return module_error.clone();
+ }
+
+ // 5-6.
+ let descendant_urls = module_tree.get_descendant_urls().borrow();
+
+ for descendant_module in descendant_urls
+ .iter()
+ // 7.
+ .filter_map(|url| module_map.get(&url.clone()))
+ {
+ // 8-2.
+ if discovered_urls.contains(&descendant_module.url) {
+ continue;
+ }
+
+ // 8-3.
+ let child_parse_error =
+ ModuleTree::find_first_parse_error(&global, &descendant_module, discovered_urls);
+
+ // 8-4.
+ if child_parse_error.is_some() {
+ return child_parse_error;
+ }
+ }
+
+ // Step 9.
+ return None;
+ }
+}
+
+#[derive(JSTraceable, MallocSizeOf)]
+struct ModuleHandler {
+ #[ignore_malloc_size_of = "Measuring trait objects is hard"]
+ task: DomRefCell<Option<Box<dyn TaskBox>>>,
+}
+
+impl ModuleHandler {
+ pub fn new(task: Box<dyn TaskBox>) -> Box<dyn Callback> {
+ Box::new(Self {
+ task: DomRefCell::new(Some(task)),
+ })
+ }
+}
+
+impl Callback for ModuleHandler {
+ fn callback(&self, _cx: *mut JSContext, _v: HandleValue) {
+ let task = self.task.borrow_mut().take().unwrap();
+ task.run_box();
+ }
+}
+
+/// The owner of the module
+/// It can be `worker` or `script` element
+#[derive(Clone)]
+pub enum ModuleOwner {
+ #[allow(dead_code)]
+ Worker(TrustedWorkerAddress),
+ Window(Trusted<HTMLScriptElement>),
+}
+
+impl ModuleOwner {
+ pub fn global(&self) -> DomRoot<GlobalScope> {
+ match &self {
+ ModuleOwner::Worker(worker) => (*worker.root().clone()).global(),
+ ModuleOwner::Window(script) => (*script.root()).global(),
+ }
+ }
+
+ fn gen_promise_with_final_handler(
+ &self,
+ module_url: Option<ServoUrl>,
+ is_top_level: bool,
+ ) -> Rc<Promise> {
+ let resolve_this = self.clone();
+ let reject_this = self.clone();
+
+ let resolved_url = module_url.clone();
+ let rejected_url = module_url.clone();
+
+ let handler = PromiseNativeHandler::new(
+ &self.global(),
+ Some(ModuleHandler::new(Box::new(
+ task!(fetched_resolve: move || {
+ resolve_this.finish_module_load(resolved_url, is_top_level);
+ }),
+ ))),
+ Some(ModuleHandler::new(Box::new(
+ task!(failure_reject: move || {
+ reject_this.finish_module_load(rejected_url, is_top_level);
+ }),
+ ))),
+ );
+
+ let compartment = enter_realm(&*self.global());
+ let comp = InCompartment::Entered(&compartment);
+ let _ais = AutoIncumbentScript::new(&*self.global());
+
+ let promise = Promise::new_in_current_compartment(&self.global(), comp);
+
+ promise.append_native_handler(&handler);
+
+ promise
+ }
+
+ /// https://html.spec.whatwg.org/multipage/#fetch-the-descendants-of-and-link-a-module-script
+ /// step 4-7.
+ pub fn finish_module_load(&self, module_url: Option<ServoUrl>, is_top_level: bool) {
+ match &self {
+ ModuleOwner::Worker(_) => unimplemented!(),
+ ModuleOwner::Window(script) => {
+ let global = self.global();
+
+ let document = document_from_node(&*script.root());
+
+ let module_map = global.get_module_map().borrow();
+
+ let (module_tree, mut load) = if let Some(script_src) = module_url.clone() {
+ let module_tree = module_map.get(&script_src.clone()).unwrap().clone();
+
+ let load = Ok(ScriptOrigin::external(
+ module_tree.get_text().borrow().clone(),
+ script_src.clone(),
+ ScriptType::Module,
+ ));
+
+ debug!(
+ "Going to finish external script from {}",
+ script_src.clone()
+ );
+
+ (module_tree, load)
+ } else {
+ let module_tree = {
+ let inline_module_map = global.get_inline_module_map().borrow();
+ inline_module_map
+ .get(&script.root().get_script_id())
+ .unwrap()
+ .clone()
+ };
+
+ let base_url = document.base_url();
+
+ let load = Ok(ScriptOrigin::internal(
+ module_tree.get_text().borrow().clone(),
+ base_url.clone(),
+ ScriptType::Module,
+ ));
+
+ debug!("Going to finish internal script from {}", base_url.clone());
+
+ (module_tree, load)
+ };
+
+ module_tree.set_status(ModuleStatus::Finished);
+
+ if !module_tree.has_all_ready_descendants(&module_map) {
+ return;
+ }
+
+ let parent_urls = module_tree.get_parent_urls().borrow();
+ let parent_all_ready = parent_urls
+ .iter()
+ .filter_map(|parent_url| module_map.get(&parent_url.clone()))
+ .all(|parent_tree| parent_tree.has_all_ready_descendants(&module_map));
+
+ if !parent_all_ready {
+ return;
+ }
+
+ parent_urls
+ .iter()
+ .filter_map(|parent_url| module_map.get(&parent_url.clone()))
+ .for_each(|parent_tree| {
+ let parent_promise = parent_tree.get_promise().borrow();
+ if let Some(promise) = parent_promise.as_ref() {
+ promise.resolve_native(&());
+ }
+ });
+
+ let mut discovered_urls: HashSet<ServoUrl> = HashSet::new();
+ let module_error =
+ ModuleTree::find_first_parse_error(&global, &module_tree, &mut discovered_urls);
+
+ match module_error {
+ None => {
+ let module_record = module_tree.get_record().borrow();
+ if let Some(record) = &*module_record {
+ let instantiated =
+ module_tree.instantiate_module_tree(&global, record.handle());
+
+ if let Err(exception) = instantiated {
+ module_tree.set_error(Some(exception.clone()));
+ }
+ }
+ },
+ Some(ModuleError::RawException(exception)) => {
+ module_tree.set_error(Some(ModuleError::RawException(exception)));
+ },
+ Some(ModuleError::Network(network_error)) => {
+ module_tree.set_error(Some(ModuleError::Network(network_error.clone())));
+
+ // Change the `result` load of the script into `network` error
+ load = Err(network_error);
+ },
+ };
+
+ if is_top_level {
+ let r#async = script
+ .root()
+ .upcast::<Element>()
+ .has_attribute(&local_name!("async"));
+
+ if !r#async && (&*script.root()).get_parser_inserted() {
+ document.deferred_script_loaded(&*script.root(), load);
+ } else if !r#async && !(&*script.root()).get_non_blocking() {
+ document.asap_in_order_script_loaded(&*script.root(), load);
+ } else {
+ document.asap_script_loaded(&*script.root(), load);
+ };
+ }
+ },
+ }
+ }
+}
+
+/// The context required for asynchronously loading an external module script source.
+struct ModuleContext {
+ /// The owner of the module that initiated the request.
+ owner: ModuleOwner,
+ /// The response body received to date.
+ data: Vec<u8>,
+ /// The response metadata received to date.
+ metadata: Option<Metadata>,
+ /// The initial URL requested.
+ url: ServoUrl,
+ /// Destination of current module context
+ destination: Destination,
+ /// Credentials Mode of current module context
+ credentials_mode: CredentialsMode,
+ /// Indicates whether the request failed, and why
+ status: Result<(), NetworkError>,
+ /// Timing object for this resource
+ resource_timing: ResourceFetchTiming,
+}
+
+impl FetchResponseListener for ModuleContext {
+ fn process_request_body(&mut self) {} // TODO(cybai): Perhaps add custom steps to perform fetch here?
+
+ fn process_request_eof(&mut self) {} // TODO(cybai): Perhaps add custom steps to perform fetch here?
+
+ fn process_response(&mut self, metadata: Result<FetchMetadata, NetworkError>) {
+ self.metadata = metadata.ok().map(|meta| match meta {
+ FetchMetadata::Unfiltered(m) => m,
+ FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
+ });
+
+ let status_code = self
+ .metadata
+ .as_ref()
+ .and_then(|m| match m.status {
+ Some((c, _)) => Some(c),
+ _ => None,
+ })
+ .unwrap_or(0);
+
+ self.status = match status_code {
+ 0 => Err(NetworkError::Internal(
+ "No http status code received".to_owned(),
+ )),
+ 200..=299 => Ok(()), // HTTP ok status codes
+ _ => Err(NetworkError::Internal(format!(
+ "HTTP error code {}",
+ status_code
+ ))),
+ };
+ }
+
+ fn process_response_chunk(&mut self, mut chunk: Vec<u8>) {
+ if self.status.is_ok() {
+ self.data.append(&mut chunk);
+ }
+ }
+
+ /// <https://html.spec.whatwg.org/multipage/#fetch-a-single-module-script>
+ /// Step 9-12
+ #[allow(unsafe_code)]
+ fn process_response_eof(&mut self, response: Result<ResourceFetchTiming, NetworkError>) {
+ let global = self.owner.global();
+
+ if let Some(window) = global.downcast::<Window>() {
+ window
+ .Document()
+ .finish_load(LoadType::Script(self.url.clone()));
+ }
+
+ // Step 9-1 & 9-2.
+ let load = response.and(self.status.clone()).and_then(|_| {
+ // Step 9-3.
+ let meta = self.metadata.take().unwrap();
+
+ if let Some(content_type) = meta.content_type.map(Serde::into_inner) {
+ let c = content_type.to_string();
+ // The MIME crate includes params (e.g. charset=utf8) in the to_string
+ // https://github.com/hyperium/mime/issues/120
+ if let Some(ty) = c.split(';').next() {
+ if !SCRIPT_JS_MIMES.contains(&ty) {
+ return Err(NetworkError::Internal(format!("Invalid MIME type: {}", ty)));
+ }
+ } else {
+ return Err(NetworkError::Internal("Empty MIME type".into()));
+ }
+ } else {
+ return Err(NetworkError::Internal("No MIME type".into()));
+ }
+
+ // Step 10.
+ let (source_text, _, _) = UTF_8.decode(&self.data);
+ Ok(ScriptOrigin::external(
+ DOMString::from(source_text),
+ meta.final_url,
+ ScriptType::Module,
+ ))
+ });
+
+ if let Err(err) = load {
+ // Step 9.
+ error!("Failed to fetch {} with error {:?}", self.url.clone(), err);
+ let module_tree = {
+ let module_map = global.get_module_map().borrow();
+ module_map.get(&self.url.clone()).unwrap().clone()
+ };
+
+ module_tree.set_status(ModuleStatus::FetchFailed);
+
+ module_tree.set_error(Some(ModuleError::Network(err)));
+
+ let promise = module_tree.get_promise().borrow();
+ promise.as_ref().unwrap().resolve_native(&());
+
+ return;
+ }
+
+ // Step 12.
+ if let Ok(ref resp_mod_script) = load {
+ let module_tree = {
+ let module_map = global.get_module_map().borrow();
+ module_map.get(&self.url.clone()).unwrap().clone()
+ };
+
+ module_tree.set_text(resp_mod_script.text());
+
+ let compiled_module = module_tree.compile_module_script(
+ &global,
+ resp_mod_script.text(),
+ self.url.clone(),
+ );
+
+ match compiled_module {
+ Err(exception) => {
+ module_tree.set_error(Some(exception));
+
+ let promise = module_tree.get_promise().borrow();
+ promise.as_ref().unwrap().resolve_native(&());
+
+ return;
+ },
+ Ok(record) => {
+ module_tree.set_record(record);
+
+ {
+ let mut visited = module_tree.get_visited_urls().borrow_mut();
+ visited.insert(self.url.clone());
+ }
+
+ let descendant_results = fetch_module_descendants_and_link(
+ &self.owner,
+ &module_tree,
+ self.destination.clone(),
+ self.credentials_mode.clone(),
+ );
+
+ // Resolve the request of this module tree promise directly
+ // when there's no descendant
+ if descendant_results.is_none() {
+ module_tree.set_status(ModuleStatus::Ready);
+
+ let promise = module_tree.get_promise().borrow();
+ promise.as_ref().unwrap().resolve_native(&());
+ }
+ },
+ }
+ }
+ }
+
+ 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) {
+ network_listener::submit_timing(self)
+ }
+}
+
+impl ResourceTimingListener for ModuleContext {
+ fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
+ let initiator_type = InitiatorType::LocalName("module".to_string());
+ (initiator_type, self.url.clone())
+ }
+
+ fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
+ self.owner.global()
+ }
+}
+
+impl PreInvoke for ModuleContext {}
+
+#[allow(unsafe_code)]
+/// A function to register module hooks (e.g. listening on resolving modules,
+/// getting module metadata, getting script private reference and resolving dynamic import)
+pub unsafe fn EnsureModuleHooksInitialized(rt: *mut JSRuntime) {
+ if GetModuleResolveHook(rt).is_some() {
+ return;
+ }
+
+ SetModuleResolveHook(rt, Some(HostResolveImportedModule));
+ SetModuleMetadataHook(rt, None);
+ SetScriptPrivateReferenceHooks(rt, None, None);
+
+ SetModuleDynamicImportHook(rt, None);
+}
+
+#[allow(unsafe_code)]
+/// https://tc39.github.io/ecma262/#sec-hostresolveimportedmodule
+/// https://html.spec.whatwg.org/multipage/#hostresolveimportedmodule(referencingscriptormodule%2C-specifier)
+unsafe extern "C" fn HostResolveImportedModule(
+ cx: *mut JSContext,
+ reference_private: RawHandleValue,
+ specifier: RawHandle<*mut JSString>,
+) -> *mut JSObject {
+ let global_scope = GlobalScope::from_context(cx);
+
+ // Step 2.
+ let mut base_url = global_scope.api_base_url();
+
+ // Step 3.
+ let module_data = (reference_private.to_private() as *const ModuleScript).as_ref();
+ if let Some(data) = module_data {
+ base_url = data.base_url.clone();
+ }
+
+ // Step 5.
+ let url = ModuleTree::resolve_module_specifier(*global_scope.get_cx(), &base_url, specifier);
+
+ // Step 6.
+ assert!(url.is_ok());
+
+ let parsed_url = url.unwrap();
+
+ // Step 4 & 7.
+ let module_map = global_scope.get_module_map().borrow();
+
+ let module_tree = module_map.get(&parsed_url);
+
+ // Step 9.
+ assert!(module_tree.is_some());
+
+ let fetched_module_object = module_tree.unwrap().get_record().borrow();
+
+ // Step 8.
+ assert!(fetched_module_object.is_some());
+
+ // Step 10.
+ if let Some(record) = &*fetched_module_object {
+ return record.handle().get();
+ }
+
+ unreachable!()
+}
+
+/// https://html.spec.whatwg.org/multipage/#fetch-a-module-script-tree
+pub fn fetch_external_module_script(
+ owner: ModuleOwner,
+ url: ServoUrl,
+ destination: Destination,
+ integrity_metadata: String,
+ credentials_mode: CredentialsMode,
+) -> Rc<Promise> {
+ // Step 1.
+ fetch_single_module_script(
+ owner,
+ url,
+ destination,
+ Referrer::Client,
+ ParserMetadata::NotParserInserted,
+ integrity_metadata,
+ credentials_mode,
+ None,
+ true,
+ )
+}
+
+/// https://html.spec.whatwg.org/multipage/#fetch-a-single-module-script
+pub fn fetch_single_module_script(
+ owner: ModuleOwner,
+ url: ServoUrl,
+ destination: Destination,
+ referrer: Referrer,
+ parser_metadata: ParserMetadata,
+ integrity_metadata: String,
+ credentials_mode: CredentialsMode,
+ parent_url: Option<ServoUrl>,
+ top_level_module_fetch: bool,
+) -> Rc<Promise> {
+ {
+ // Step 1.
+ let global = owner.global();
+ let module_map = global.get_module_map().borrow();
+
+ debug!("Start to fetch {}", url);
+
+ if let Some(module_tree) = module_map.get(&url.clone()) {
+ let status = module_tree.get_status();
+
+ let promise = module_tree.get_promise().borrow();
+
+ debug!("Meet a fetched url {} and its status is {:?}", url, status);
+
+ assert!(promise.is_some());
+
+ module_tree.append_handler(owner.clone(), url.clone(), top_level_module_fetch);
+
+ let promise = promise.as_ref().unwrap();
+
+ match status {
+ ModuleStatus::Initial => unreachable!(
+ "We have the module in module map so its status should not be `initial`"
+ ),
+ // Step 2.
+ ModuleStatus::Fetching => return promise.clone(),
+ ModuleStatus::FetchingDescendants => {
+ if module_tree.has_all_ready_descendants(&module_map) {
+ promise.resolve_native(&());
+ }
+ },
+ // Step 3.
+ ModuleStatus::FetchFailed | ModuleStatus::Ready | ModuleStatus::Finished => {
+ promise.resolve_native(&());
+ },
+ }
+
+ return promise.clone();
+ }
+ }
+
+ let global = owner.global();
+
+ let module_tree = ModuleTree::new(url.clone());
+ module_tree.set_status(ModuleStatus::Fetching);
+
+ let promise = owner.gen_promise_with_final_handler(Some(url.clone()), top_level_module_fetch);
+
+ module_tree.set_promise(promise.clone());
+ if let Some(parent_url) = parent_url {
+ module_tree.insert_parent_url(parent_url);
+ }
+
+ // Step 4.
+ global.set_module_map(url.clone(), module_tree);
+
+ // Step 5-6.
+ let mode = match destination.clone() {
+ Destination::Worker | Destination::SharedWorker if top_level_module_fetch => {
+ RequestMode::SameOrigin
+ },
+ _ => RequestMode::CorsMode,
+ };
+
+ let document: Option<DomRoot<Document>> = match &owner {
+ ModuleOwner::Worker(_) => None,
+ ModuleOwner::Window(script) => Some(document_from_node(&*script.root())),
+ };
+
+ // Step 7-8.
+ let request = RequestBuilder::new(url.clone())
+ .destination(destination.clone())
+ .origin(global.origin().immutable().clone())
+ .referrer(Some(referrer))
+ .parser_metadata(parser_metadata)
+ .integrity_metadata(integrity_metadata.clone())
+ .credentials_mode(credentials_mode)
+ .mode(mode);
+
+ let context = Arc::new(Mutex::new(ModuleContext {
+ owner,
+ data: vec![],
+ metadata: None,
+ url: url.clone(),
+ destination: destination.clone(),
+ credentials_mode: credentials_mode.clone(),
+ status: Ok(()),
+ resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource),
+ }));
+
+ let (action_sender, action_receiver) = ipc::channel().unwrap();
+
+ let listener = NetworkListener {
+ context,
+ task_source: global.networking_task_source(),
+ canceller: Some(global.task_canceller(TaskSourceName::Networking)),
+ };
+
+ ROUTER.add_route(
+ action_receiver.to_opaque(),
+ Box::new(move |message| {
+ listener.notify_fetch(message.to().unwrap());
+ }),
+ );
+
+ if let Some(doc) = document {
+ doc.fetch_async(LoadType::Script(url), request, action_sender);
+ }
+
+ promise
+}
+
+#[allow(unsafe_code)]
+/// https://html.spec.whatwg.org/multipage/#fetch-an-inline-module-script-graph
+pub fn fetch_inline_module_script(
+ owner: ModuleOwner,
+ module_script_text: DOMString,
+ url: ServoUrl,
+ script_id: ScriptId,
+ credentials_mode: CredentialsMode,
+) {
+ let global = owner.global();
+
+ let module_tree = ModuleTree::new(url.clone());
+
+ let promise = owner.gen_promise_with_final_handler(None, true);
+
+ module_tree.set_promise(promise.clone());
+
+ let compiled_module =
+ module_tree.compile_module_script(&global, module_script_text, url.clone());
+
+ match compiled_module {
+ Ok(record) => {
+ module_tree.set_record(record);
+
+ let descendant_results = fetch_module_descendants_and_link(
+ &owner,
+ &module_tree,
+ Destination::Script,
+ credentials_mode,
+ );
+
+ global.set_inline_module_map(script_id, module_tree);
+
+ if descendant_results.is_none() {
+ promise.resolve_native(&());
+ }
+ },
+ Err(exception) => {
+ module_tree.set_status(ModuleStatus::Ready);
+ module_tree.set_error(Some(exception));
+ global.set_inline_module_map(script_id, module_tree);
+ promise.resolve_native(&());
+ },
+ }
+}
+
+/// https://html.spec.whatwg.org/multipage/#fetch-the-descendants-of-and-link-a-module-script
+/// Step 1-3.
+#[allow(unsafe_code)]
+fn fetch_module_descendants_and_link(
+ owner: &ModuleOwner,
+ module_tree: &ModuleTree,
+ destination: Destination,
+ credentials_mode: CredentialsMode,
+) -> Option<Rc<Promise>> {
+ let descendant_results =
+ fetch_module_descendants(owner, module_tree, destination, credentials_mode);
+
+ match descendant_results {
+ Ok(descendants) => {
+ if descendants.len() > 0 {
+ unsafe {
+ let global = owner.global();
+
+ let _compartment = enter_realm(&*global);
+ AlreadyInCompartment::assert(&*global);
+ let _ais = AutoIncumbentScript::new(&*global);
+
+ let abv = CreateAutoObjectVector(*global.get_cx());
+
+ for descendant in descendants {
+ assert!(AppendToAutoObjectVector(
+ abv as *mut AutoObjectVector,
+ descendant.promise_obj().get()
+ ));
+ }
+
+ rooted!(in(*global.get_cx()) let raw_promise_all = GetWaitForAllPromise(*global.get_cx(), abv));
+
+ let promise_all =
+ Promise::new_with_js_promise(raw_promise_all.handle(), global.get_cx());
+
+ let promise = module_tree.get_promise().borrow();
+ let promise = promise.as_ref().unwrap().clone();
+
+ let resolve_promise = TrustedPromise::new(promise.clone());
+ let reject_promise = TrustedPromise::new(promise.clone());
+
+ let handler = PromiseNativeHandler::new(
+ &global,
+ Some(ModuleHandler::new(Box::new(
+ task!(all_fetched_resolve: move || {
+ let promise = resolve_promise.root();
+ promise.resolve_native(&());
+ }),
+ ))),
+ Some(ModuleHandler::new(Box::new(
+ task!(all_failure_reject: move || {
+ let promise = reject_promise.root();
+ promise.reject_native(&());
+ }),
+ ))),
+ );
+
+ promise_all.append_native_handler(&handler);
+
+ return Some(promise_all);
+ }
+ }
+ },
+ Err(err) => {
+ module_tree.set_error(Some(err));
+ },
+ }
+
+ None
+}
+
+#[allow(unsafe_code)]
+/// https://html.spec.whatwg.org/multipage/#fetch-the-descendants-of-a-module-script
+fn fetch_module_descendants(
+ owner: &ModuleOwner,
+ module_tree: &ModuleTree,
+ destination: Destination,
+ credentials_mode: CredentialsMode,
+) -> Result<Vec<Rc<Promise>>, ModuleError> {
+ debug!("Start to load dependencies of {}", module_tree.url.clone());
+
+ let global = owner.global();
+
+ module_tree.set_status(ModuleStatus::FetchingDescendants);
+
+ module_tree
+ .resolve_requested_modules(&global)
+ .map(|requested_urls| {
+ module_tree.append_descendant_urls(requested_urls.clone());
+
+ let parent_urls = module_tree.get_parent_urls().borrow();
+
+ if parent_urls.intersection(&requested_urls).count() > 0 {
+ return Vec::new();
+ }
+
+ requested_urls
+ .iter()
+ .map(|requested_url| {
+ // https://html.spec.whatwg.org/multipage/#internal-module-script-graph-fetching-procedure
+ // Step 1.
+ {
+ let visited = module_tree.get_visited_urls().borrow();
+ assert!(visited.get(&requested_url).is_some());
+ }
+
+ // Step 2.
+ fetch_single_module_script(
+ owner.clone(),
+ requested_url.clone(),
+ destination.clone(),
+ Referrer::Client,
+ ParserMetadata::NotParserInserted,
+ "".to_owned(),
+ credentials_mode.clone(),
+ Some(module_tree.url.clone()),
+ false,
+ )
+ })
+ .collect()
+ })
+}
diff --git a/components/script/script_runtime.rs b/components/script/script_runtime.rs
index 02a6b42b854..284c49312c6 100644
--- a/components/script/script_runtime.rs
+++ b/components/script/script_runtime.rs
@@ -30,6 +30,7 @@ use crate::dom::promise::Promise;
use crate::dom::promiserejectionevent::PromiseRejectionEvent;
use crate::dom::response::Response;
use crate::microtask::{EnqueuedPromiseCallback, Microtask, MicrotaskQueue};
+use crate::script_module::EnsureModuleHooksInitialized;
use crate::script_thread::trace_thread;
use crate::task::TaskBox;
use crate::task_source::networking::NetworkingTaskSource;
@@ -498,6 +499,8 @@ unsafe fn new_rt_and_cx_with_parent(
SetJobQueue(cx, job_queue);
SetPromiseRejectionTrackerCallback(cx, Some(promise_rejection_tracker), ptr::null_mut());
+ EnsureModuleHooksInitialized(runtime.rt());
+
set_gc_zeal_options(cx);
// Enable or disable the JITs.
@@ -915,16 +918,13 @@ unsafe extern "C" fn consume_stream(
let mimetype = unwrapped_source.Headers().extract_mime_type();
//Step 2.3 If mimeType is not `application/wasm`, return with a TypeError and abort these substeps.
- match &mimetype[..] {
- b"application/wasm" | b"APPLICATION/wasm" | b"APPLICATION/WASM" => {},
- _ => {
- throw_dom_exception(
- cx,
- &global,
- Error::Type("Response has unsupported MIME type".to_string()),
- );
- return false;
- },
+ if !&mimetype[..].eq_ignore_ascii_case(b"application/wasm") {
+ throw_dom_exception(
+ cx,
+ &global,
+ Error::Type("Response has unsupported MIME type".to_string()),
+ );
+ return false;
}
//Step 2.4 If response is not CORS-same-origin, return with a TypeError and abort these substeps.
diff --git a/components/script_plugins/lib.rs b/components/script_plugins/lib.rs
index ea2b0fa74a6..f686f64f6fe 100644
--- a/components/script_plugins/lib.rs
+++ b/components/script_plugins/lib.rs
@@ -216,8 +216,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnrootedPass {
let parent_item = map.expect_item(map.get_parent_item(var.id));
if !has_lint_attr(&self.symbols, &parent_item.attrs, self.symbols.must_root) {
match var.data {
- hir::VariantData::Tuple(ref fields, ..) => {
- for ref field in fields {
+ hir::VariantData::Tuple(fields, ..) => {
+ for field in fields {
let def_id = cx.tcx.hir().local_def_id(field.hir_id);
if is_unrooted_ty(&self.symbols, cx, cx.tcx.type_of(def_id), false) {
cx.span_lint(
diff --git a/components/style/properties/longhands/border.mako.rs b/components/style/properties/longhands/border.mako.rs
index 09cbea19a7f..f281fa1a30d 100644
--- a/components/style/properties/longhands/border.mako.rs
+++ b/components/style/properties/longhands/border.mako.rs
@@ -75,7 +75,7 @@
"BorderCornerRadius",
"computed::BorderCornerRadius::zero()",
"parse",
- engines="gecko servo-2013",
+ engines="gecko servo-2013 servo-2020",
extra_prefixes=prefixes,
spec=maybe_logical_spec(corner, "radius"),
boxed=True,
diff --git a/components/style/properties/shorthands/border.mako.rs b/components/style/properties/shorthands/border.mako.rs
index d584e568aed..1f77b905021 100644
--- a/components/style/properties/shorthands/border.mako.rs
+++ b/components/style/properties/shorthands/border.mako.rs
@@ -239,7 +239,7 @@ pub fn parse_border<'i, 't>(
<%helpers:shorthand
name="border-radius"
- engines="gecko servo-2013"
+ engines="gecko servo-2013 servo-2020"
sub_properties="${' '.join(
'border-%s-radius' % (corner)
for corner in ['top-left', 'top-right', 'bottom-right', 'bottom-left']