aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom')
-rw-r--r--components/script/dom/canvasrenderingcontext2d.rs36
-rw-r--r--components/script/dom/dissimilaroriginwindow.rs7
-rw-r--r--components/script/dom/document.rs445
-rw-r--r--components/script/dom/globalscope.rs199
-rw-r--r--components/script/dom/htmldetailselement.rs4
-rw-r--r--components/script/dom/htmlelement.rs9
-rw-r--r--components/script/dom/htmlformelement.rs10
-rw-r--r--components/script/dom/htmlscriptelement.rs2
-rw-r--r--components/script/dom/messageport.rs38
-rw-r--r--components/script/dom/node.rs50
-rw-r--r--components/script/dom/offscreencanvasrenderingcontext2d.rs2
-rw-r--r--components/script/dom/readablestream.rs8
-rw-r--r--components/script/dom/underlyingsourcecontainer.rs2
-rw-r--r--components/script/dom/window.rs26
-rw-r--r--components/script/dom/windowproxy.rs17
-rw-r--r--components/script/dom/worker.rs12
-rw-r--r--components/script/dom/writablestream.rs4
-rw-r--r--components/script/dom/writablestreamdefaultcontroller.rs10
18 files changed, 655 insertions, 226 deletions
diff --git a/components/script/dom/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs
index 73052e6906e..38bd38ad511 100644
--- a/components/script/dom/canvasrenderingcontext2d.rs
+++ b/components/script/dom/canvasrenderingcontext2d.rs
@@ -4,7 +4,7 @@
use canvas_traits::canvas::{Canvas2dMsg, CanvasId, CanvasMsg, FromScriptMsg};
use dom_struct::dom_struct;
-use euclid::default::{Point2D, Rect, Size2D};
+use euclid::default::Size2D;
use profile_traits::ipc;
use script_bindings::inheritance::Castable;
use script_layout_interface::HTMLCanvasDataSource;
@@ -74,23 +74,12 @@ impl CanvasRenderingContext2D {
reflect_dom_object(boxed, global, can_gc)
}
- // https://html.spec.whatwg.org/multipage/#concept-canvas-set-bitmap-dimensions
- pub(crate) fn set_bitmap_dimensions(&self, size: Size2D<u32>) {
- self.reset_to_initial_state();
- self.canvas_state
- .get_ipc_renderer()
- .send(CanvasMsg::Recreate(
- Some(size.to_u64()),
- self.canvas_state.get_canvas_id(),
- ))
- .unwrap();
- }
-
// https://html.spec.whatwg.org/multipage/#reset-the-rendering-context-to-its-default-state
fn reset_to_initial_state(&self) {
self.canvas_state.reset_to_initial_state();
}
+ /// <https://html.spec.whatwg.org/multipage/#concept-canvas-set-bitmap-dimensions>
pub(crate) fn set_canvas_bitmap_dimensions(&self, size: Size2D<u64>) {
self.canvas_state.set_bitmap_dimensions(size);
}
@@ -106,20 +95,17 @@ impl CanvasRenderingContext2D {
pub(crate) fn send_canvas_2d_msg(&self, msg: Canvas2dMsg) {
self.canvas_state.send_canvas_2d_msg(msg)
}
-
- pub(crate) fn get_rect(&self, rect: Rect<u32>) -> Vec<u8> {
- let rect = Rect::new(
- Point2D::new(rect.origin.x as u64, rect.origin.y as u64),
- Size2D::new(rect.size.width as u64, rect.size.height as u64),
- );
- self.canvas_state.get_rect(self.canvas.size(), rect)
- }
}
impl LayoutCanvasRenderingContextHelpers for LayoutDom<'_, CanvasRenderingContext2D> {
fn canvas_data_source(self) -> HTMLCanvasDataSource {
let canvas_state = &self.unsafe_get().canvas_state;
- HTMLCanvasDataSource::Image(canvas_state.image_key())
+
+ if canvas_state.is_paintable() {
+ HTMLCanvasDataSource::Image(canvas_state.image_key())
+ } else {
+ HTMLCanvasDataSource::Empty
+ }
}
}
@@ -139,13 +125,11 @@ impl CanvasContext for CanvasRenderingContext2D {
}
fn resize(&self) {
- self.set_bitmap_dimensions(self.size().cast())
+ self.set_canvas_bitmap_dimensions(self.size().cast())
}
fn get_image_data(&self) -> Option<Snapshot> {
- let size = self.size();
-
- if size.is_empty() {
+ if !self.canvas_state.is_paintable() {
return None;
}
diff --git a/components/script/dom/dissimilaroriginwindow.rs b/components/script/dom/dissimilaroriginwindow.rs
index b7fbe0855fe..70c384db822 100644
--- a/components/script/dom/dissimilaroriginwindow.rs
+++ b/components/script/dom/dissimilaroriginwindow.rs
@@ -181,12 +181,13 @@ impl DissimilarOriginWindowMethods<crate::DomTypeHolder> for DissimilarOriginWin
// https://html.spec.whatwg.org/multipage/#dom-window-blur
fn Blur(&self) {
- // TODO: Implement x-origin blur
+ // > User agents are encouraged to ignore calls to this `blur()` method
+ // > entirely.
}
- // https://html.spec.whatwg.org/multipage/#dom-focus
+ // https://html.spec.whatwg.org/multipage/#dom-window-focus
fn Focus(&self) {
- // TODO: Implement x-origin focus
+ self.window_proxy().focus();
}
// https://html.spec.whatwg.org/multipage/#dom-location
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs
index 1e2a3747751..2baab15e1b8 100644
--- a/components/script/dom/document.rs
+++ b/components/script/dom/document.rs
@@ -30,8 +30,8 @@ use devtools_traits::ScriptToDevtoolsControlMsg;
use dom_struct::dom_struct;
use embedder_traits::{
AllowOrDeny, AnimationState, CompositorHitTestResult, ContextMenuResult, EditingActionEvent,
- EmbedderMsg, ImeEvent, InputEvent, LoadStatus, MouseButton, MouseButtonAction,
- MouseButtonEvent, TouchEvent, TouchEventType, TouchId, WheelEvent,
+ EmbedderMsg, FocusSequenceNumber, ImeEvent, InputEvent, LoadStatus, MouseButton,
+ MouseButtonAction, MouseButtonEvent, TouchEvent, TouchEventType, TouchId, WheelEvent,
};
use encoding_rs::{Encoding, UTF_8};
use euclid::default::{Point2D, Rect, Size2D};
@@ -270,12 +270,11 @@ pub(crate) enum IsHTMLDocument {
#[derive(JSTraceable, MallocSizeOf)]
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
-enum FocusTransaction {
- /// No focus operation is in effect.
- NotInTransaction,
- /// A focus operation is in effect.
- /// Contains the element that has most recently requested focus for itself.
- InTransaction(Option<Dom<Element>>),
+struct FocusTransaction {
+ /// The focused element of this document.
+ element: Option<Dom<Element>>,
+ /// See [`Document::has_focus`].
+ has_focus: bool,
}
/// Information about a declarative refresh
@@ -341,9 +340,16 @@ pub(crate) struct Document {
/// Whether the DOMContentLoaded event has already been dispatched.
domcontentloaded_dispatched: Cell<bool>,
/// The state of this document's focus transaction.
- focus_transaction: DomRefCell<FocusTransaction>,
+ focus_transaction: DomRefCell<Option<FocusTransaction>>,
/// The element that currently has the document focus context.
focused: MutNullableDom<Element>,
+ /// The last sequence number sent to the constellation.
+ #[no_trace]
+ focus_sequence: Cell<FocusSequenceNumber>,
+ /// Indicates whether the container is included in the top-level browsing
+ /// context's focus chain (not considering system focus). Permanently `true`
+ /// for a top-level document.
+ has_focus: Cell<bool>,
/// The script element that is currently executing.
current_script: MutNullableDom<HTMLScriptElement>,
/// <https://html.spec.whatwg.org/multipage/#pending-parsing-blocking-script>
@@ -1120,122 +1126,318 @@ impl Document {
self.focused.get()
}
+ /// Get the last sequence number sent to the constellation.
+ ///
+ /// Received focus-related messages with sequence numbers less than the one
+ /// returned by this method must be discarded.
+ pub fn get_focus_sequence(&self) -> FocusSequenceNumber {
+ self.focus_sequence.get()
+ }
+
+ /// Generate the next sequence number for focus-related messages.
+ fn increment_fetch_focus_sequence(&self) -> FocusSequenceNumber {
+ self.focus_sequence.set(FocusSequenceNumber(
+ self.focus_sequence
+ .get()
+ .0
+ .checked_add(1)
+ .expect("too many focus messages have been sent"),
+ ));
+ self.focus_sequence.get()
+ }
+
/// Initiate a new round of checking for elements requesting focus. The last element to call
/// `request_focus` before `commit_focus_transaction` is called will receive focus.
fn begin_focus_transaction(&self) {
- *self.focus_transaction.borrow_mut() = FocusTransaction::InTransaction(Default::default());
+ // Initialize it with the current state
+ *self.focus_transaction.borrow_mut() = Some(FocusTransaction {
+ element: self.focused.get().as_deref().map(Dom::from_ref),
+ has_focus: self.has_focus.get(),
+ });
}
/// <https://html.spec.whatwg.org/multipage/#focus-fixup-rule>
pub(crate) fn perform_focus_fixup_rule(&self, not_focusable: &Element, can_gc: CanGc) {
+ // Return if `not_focusable` is not the designated focused area of the
+ // `Document`.
if Some(not_focusable) != self.focused.get().as_deref() {
return;
}
- self.request_focus(
- self.GetBody().as_ref().map(|e| e.upcast()),
- FocusType::Element,
- can_gc,
- )
+
+ let implicit_transaction = self.focus_transaction.borrow().is_none();
+
+ if implicit_transaction {
+ self.begin_focus_transaction();
+ }
+
+ // Designate the viewport as the new focused area of the `Document`, but
+ // do not run the focusing steps.
+ {
+ let mut focus_transaction = self.focus_transaction.borrow_mut();
+ focus_transaction.as_mut().unwrap().element = None;
+ }
+
+ if implicit_transaction {
+ self.commit_focus_transaction(FocusInitiator::Local, can_gc);
+ }
}
- /// Request that the given element receive focus once the current transaction is complete.
- /// If None is passed, then whatever element is currently focused will no longer be focused
- /// once the transaction is complete.
+ /// Request that the given element receive focus once the current
+ /// transaction is complete. `None` specifies to focus the document.
+ ///
+ /// If there's no ongoing transaction, this method automatically starts and
+ /// commits an implicit transaction.
pub(crate) fn request_focus(
&self,
elem: Option<&Element>,
- focus_type: FocusType,
+ focus_initiator: FocusInitiator,
can_gc: CanGc,
) {
- let implicit_transaction = matches!(
- *self.focus_transaction.borrow(),
- FocusTransaction::NotInTransaction
- );
+ // If an element is specified, and it's non-focusable, ignore the
+ // request.
+ if elem.is_some_and(|e| !e.is_focusable_area()) {
+ return;
+ }
+
+ let implicit_transaction = self.focus_transaction.borrow().is_none();
+
if implicit_transaction {
self.begin_focus_transaction();
}
- if elem.is_none_or(|e| e.is_focusable_area()) {
- *self.focus_transaction.borrow_mut() =
- FocusTransaction::InTransaction(elem.map(Dom::from_ref));
+
+ {
+ let mut focus_transaction = self.focus_transaction.borrow_mut();
+ let focus_transaction = focus_transaction.as_mut().unwrap();
+ focus_transaction.element = elem.map(Dom::from_ref);
+ focus_transaction.has_focus = true;
}
+
if implicit_transaction {
- self.commit_focus_transaction(focus_type, can_gc);
+ self.commit_focus_transaction(focus_initiator, can_gc);
+ }
+ }
+
+ /// Update the local focus state accordingly after being notified that the
+ /// document's container is removed from the top-level browsing context's
+ /// focus chain (not considering system focus).
+ pub(crate) fn handle_container_unfocus(&self, can_gc: CanGc) {
+ assert!(
+ self.window().parent_info().is_some(),
+ "top-level document cannot be unfocused",
+ );
+
+ // Since this method is called from an event loop, there mustn't be
+ // an in-progress focus transaction
+ assert!(
+ self.focus_transaction.borrow().is_none(),
+ "there mustn't be an in-progress focus transaction at this point"
+ );
+
+ // Start an implicit focus transaction
+ self.begin_focus_transaction();
+
+ // Update the transaction
+ {
+ let mut focus_transaction = self.focus_transaction.borrow_mut();
+ focus_transaction.as_mut().unwrap().has_focus = false;
}
+
+ // Commit the implicit focus transaction
+ self.commit_focus_transaction(FocusInitiator::Remote, can_gc);
}
/// Reassign the focus context to the element that last requested focus during this
- /// transaction, or none if no elements requested it.
- fn commit_focus_transaction(&self, focus_type: FocusType, can_gc: CanGc) {
- let possibly_focused = match *self.focus_transaction.borrow() {
- FocusTransaction::NotInTransaction => unreachable!(),
- FocusTransaction::InTransaction(ref elem) => {
- elem.as_ref().map(|e| DomRoot::from_ref(&**e))
- },
+ /// transaction, or the document if no elements requested it.
+ fn commit_focus_transaction(&self, focus_initiator: FocusInitiator, can_gc: CanGc) {
+ let (mut new_focused, new_focus_state) = {
+ let focus_transaction = self.focus_transaction.borrow();
+ let focus_transaction = focus_transaction
+ .as_ref()
+ .expect("no focus transaction in progress");
+ (
+ focus_transaction
+ .element
+ .as_ref()
+ .map(|e| DomRoot::from_ref(&**e)),
+ focus_transaction.has_focus,
+ )
};
- *self.focus_transaction.borrow_mut() = FocusTransaction::NotInTransaction;
- if self.focused == possibly_focused.as_deref() {
- return;
+ *self.focus_transaction.borrow_mut() = None;
+
+ if !new_focus_state {
+ // In many browsers, a document forgets its focused area when the
+ // document is removed from the top-level BC's focus chain
+ if new_focused.take().is_some() {
+ trace!(
+ "Forgetting the document's focused area because the \
+ document's container was removed from the top-level BC's \
+ focus chain"
+ );
+ }
}
- if let Some(ref elem) = self.focused.get() {
- let node = elem.upcast::<Node>();
- elem.set_focus_state(false);
- // FIXME: pass appropriate relatedTarget
- self.fire_focus_event(FocusEventType::Blur, node, None, can_gc);
- // Notify the embedder to hide the input method.
- if elem.input_method_type().is_some() {
- self.send_to_embedder(EmbedderMsg::HideIME(self.webview_id()));
+ let old_focused = self.focused.get();
+ let old_focus_state = self.has_focus.get();
+
+ debug!(
+ "Committing focus transaction: {:?} → {:?}",
+ (&old_focused, old_focus_state),
+ (&new_focused, new_focus_state),
+ );
+
+ // `*_focused_filtered` indicates the local element (if any) included in
+ // the top-level BC's focus chain.
+ let old_focused_filtered = old_focused.as_ref().filter(|_| old_focus_state);
+ let new_focused_filtered = new_focused.as_ref().filter(|_| new_focus_state);
+
+ let trace_focus_chain = |name, element, doc| {
+ trace!(
+ "{} local focus chain: {}",
+ name,
+ match (element, doc) {
+ (Some(e), _) => format!("[{:?}, document]", e),
+ (None, true) => "[document]".to_owned(),
+ (None, false) => "[]".to_owned(),
+ }
+ );
+ };
+
+ trace_focus_chain("Old", old_focused_filtered, old_focus_state);
+ trace_focus_chain("New", new_focused_filtered, new_focus_state);
+
+ if old_focused_filtered != new_focused_filtered {
+ if let Some(elem) = &old_focused_filtered {
+ let node = elem.upcast::<Node>();
+ elem.set_focus_state(false);
+ // FIXME: pass appropriate relatedTarget
+ if node.is_connected() {
+ self.fire_focus_event(FocusEventType::Blur, node.upcast(), None, can_gc);
+ }
+
+ // Notify the embedder to hide the input method.
+ if elem.input_method_type().is_some() {
+ self.send_to_embedder(EmbedderMsg::HideIME(self.webview_id()));
+ }
}
}
- self.focused.set(possibly_focused.as_deref());
+ if old_focus_state != new_focus_state && !new_focus_state {
+ self.fire_focus_event(FocusEventType::Blur, self.global().upcast(), None, can_gc);
+ }
- if let Some(ref elem) = self.focused.get() {
- elem.set_focus_state(true);
- let node = elem.upcast::<Node>();
- // FIXME: pass appropriate relatedTarget
- self.fire_focus_event(FocusEventType::Focus, node, None, can_gc);
- // Update the focus state for all elements in the focus chain.
- // https://html.spec.whatwg.org/multipage/#focus-chain
- if focus_type == FocusType::Element {
- self.window()
- .send_to_constellation(ScriptToConstellationMessage::Focus);
+ self.focused.set(new_focused.as_deref());
+ self.has_focus.set(new_focus_state);
+
+ if old_focus_state != new_focus_state && new_focus_state {
+ self.fire_focus_event(FocusEventType::Focus, self.global().upcast(), None, can_gc);
+ }
+
+ if old_focused_filtered != new_focused_filtered {
+ if let Some(elem) = &new_focused_filtered {
+ elem.set_focus_state(true);
+ let node = elem.upcast::<Node>();
+ // FIXME: pass appropriate relatedTarget
+ self.fire_focus_event(FocusEventType::Focus, node.upcast(), None, can_gc);
+
+ // Notify the embedder to display an input method.
+ if let Some(kind) = elem.input_method_type() {
+ let rect = elem.upcast::<Node>().bounding_content_box_or_zero(can_gc);
+ let rect = Rect::new(
+ Point2D::new(rect.origin.x.to_px(), rect.origin.y.to_px()),
+ Size2D::new(rect.size.width.to_px(), rect.size.height.to_px()),
+ );
+ let (text, multiline) = if let Some(input) = elem.downcast::<HTMLInputElement>()
+ {
+ (
+ Some((
+ (input.Value()).to_string(),
+ input.GetSelectionEnd().unwrap_or(0) as i32,
+ )),
+ false,
+ )
+ } else if let Some(textarea) = elem.downcast::<HTMLTextAreaElement>() {
+ (
+ Some((
+ (textarea.Value()).to_string(),
+ textarea.GetSelectionEnd().unwrap_or(0) as i32,
+ )),
+ true,
+ )
+ } else {
+ (None, false)
+ };
+ self.send_to_embedder(EmbedderMsg::ShowIME(
+ self.webview_id(),
+ kind,
+ text,
+ multiline,
+ DeviceIntRect::from_untyped(&rect.to_box2d()),
+ ));
+ }
}
+ }
+
+ if focus_initiator != FocusInitiator::Local {
+ return;
+ }
- // Notify the embedder to display an input method.
- if let Some(kind) = elem.input_method_type() {
- let rect = elem.upcast::<Node>().bounding_content_box_or_zero(can_gc);
- let rect = Rect::new(
- Point2D::new(rect.origin.x.to_px(), rect.origin.y.to_px()),
- Size2D::new(rect.size.width.to_px(), rect.size.height.to_px()),
+ // We are the initiator of the focus operation, so we must broadcast
+ // the change we intend to make.
+ match (old_focus_state, new_focus_state) {
+ (_, true) => {
+ // Advertise the change in the focus chain.
+ // <https://html.spec.whatwg.org/multipage/#focus-chain>
+ // <https://html.spec.whatwg.org/multipage/#focusing-steps>
+ //
+ // If the top-level BC doesn't have system focus, this won't
+ // have an immediate effect, but it will when we gain system
+ // focus again. Therefore we still have to send `ScriptMsg::
+ // Focus`.
+ //
+ // When a container with a non-null nested browsing context is
+ // focused, its active document becomes the focused area of the
+ // top-level browsing context instead. Therefore we need to let
+ // the constellation know if such a container is focused.
+ //
+ // > The focusing steps for an object `new focus target` [...]
+ // >
+ // > 3. If `new focus target` is a browsing context container
+ // > with non-null nested browsing context, then set
+ // > `new focus target` to the nested browsing context's
+ // > active document.
+ let child_browsing_context_id = new_focused
+ .as_ref()
+ .and_then(|elem| elem.downcast::<HTMLIFrameElement>())
+ .and_then(|iframe| iframe.browsing_context_id());
+
+ let sequence = self.increment_fetch_focus_sequence();
+
+ debug!(
+ "Advertising the focus request to the constellation \
+ with sequence number {} and child BC ID {}",
+ sequence,
+ child_browsing_context_id
+ .as_ref()
+ .map(|id| id as &dyn std::fmt::Display)
+ .unwrap_or(&"(none)"),
);
- let (text, multiline) = if let Some(input) = elem.downcast::<HTMLInputElement>() {
- (
- Some((
- input.Value().to_string(),
- input.GetSelectionEnd().unwrap_or(0) as i32,
- )),
- false,
- )
- } else if let Some(textarea) = elem.downcast::<HTMLTextAreaElement>() {
- (
- Some((
- textarea.Value().to_string(),
- textarea.GetSelectionEnd().unwrap_or(0) as i32,
- )),
- true,
- )
- } else {
- (None, false)
- };
- self.send_to_embedder(EmbedderMsg::ShowIME(
- self.webview_id(),
- kind,
- text,
- multiline,
- DeviceIntRect::from_untyped(&rect.to_box2d()),
- ));
- }
+
+ self.window()
+ .send_to_constellation(ScriptToConstellationMessage::Focus(
+ child_browsing_context_id,
+ sequence,
+ ));
+ },
+ (false, false) => {
+ // Our `Document` doesn't have focus, and we intend to keep it
+ // this way.
+ },
+ (true, false) => {
+ unreachable!(
+ "Can't lose the document's focus without specifying \
+ another one to focus"
+ );
+ },
}
}
@@ -1350,7 +1552,10 @@ impl Document {
}
self.begin_focus_transaction();
- self.request_focus(Some(&*el), FocusType::Element, can_gc);
+ // Try to focus `el`. If it's not focusable, focus the document
+ // instead.
+ self.request_focus(None, FocusInitiator::Local, can_gc);
+ self.request_focus(Some(&*el), FocusInitiator::Local, can_gc);
}
let dom_event = DomRoot::upcast::<Event>(MouseEvent::for_platform_mouse_event(
@@ -1388,7 +1593,9 @@ impl Document {
}
if let MouseButtonAction::Click = event.action {
- self.commit_focus_transaction(FocusType::Element, can_gc);
+ if self.focus_transaction.borrow().is_some() {
+ self.commit_focus_transaction(FocusInitiator::Local, can_gc);
+ }
self.maybe_fire_dblclick(
hit_test_result.point_in_viewport,
node,
@@ -2215,7 +2422,7 @@ impl Document {
ImeEvent::Dismissed => {
self.request_focus(
self.GetBody().as_ref().map(|e| e.upcast()),
- FocusType::Element,
+ FocusInitiator::Local,
can_gc,
);
return;
@@ -3194,7 +3401,7 @@ impl Document {
fn fire_focus_event(
&self,
focus_event_type: FocusEventType,
- node: &Node,
+ event_target: &EventTarget,
related_target: Option<&EventTarget>,
can_gc: CanGc,
) {
@@ -3214,8 +3421,7 @@ impl Document {
);
let event = event.upcast::<Event>();
event.set_trusted(true);
- let target = node.upcast();
- event.fire(target, can_gc);
+ event.fire(event_target, can_gc);
}
/// <https://html.spec.whatwg.org/multipage/#cookie-averse-document-object>
@@ -3795,6 +4001,8 @@ impl Document {
.and_then(|charset| Encoding::for_label(charset.as_bytes()))
.unwrap_or(UTF_8);
+ let has_focus = window.parent_info().is_none();
+
let has_browsing_context = has_browsing_context == HasBrowsingContext::Yes;
Document {
@@ -3842,8 +4050,10 @@ impl Document {
stylesheet_list: MutNullableDom::new(None),
ready_state: Cell::new(ready_state),
domcontentloaded_dispatched: Cell::new(domcontentloaded_dispatched),
- focus_transaction: DomRefCell::new(FocusTransaction::NotInTransaction),
+ focus_transaction: DomRefCell::new(None),
focused: Default::default(),
+ focus_sequence: Cell::new(FocusSequenceNumber::default()),
+ has_focus: Cell::new(has_focus),
current_script: Default::default(),
pending_parsing_blocking_script: Default::default(),
script_blocking_stylesheets_count: Cell::new(0u32),
@@ -4989,12 +5199,34 @@ impl DocumentMethods<crate::DomTypeHolder> for Document {
// https://html.spec.whatwg.org/multipage/#dom-document-hasfocus
fn HasFocus(&self) -> bool {
- // Step 1-2.
- if self.window().parent_info().is_none() && self.is_fully_active() {
- return true;
+ // <https://html.spec.whatwg.org/multipage/#has-focus-steps>
+ //
+ // > The has focus steps, given a `Document` object `target`, are as
+ // > follows:
+ // >
+ // > 1. If `target`'s browsing context's top-level browsing context does
+ // > not have system focus, then return false.
+
+ // > 2. Let `candidate` be `target`'s browsing context's top-level
+ // > browsing context's active document.
+ // >
+ // > 3. While true:
+ // >
+ // > 3.1. If `candidate` is target, then return true.
+ // >
+ // > 3.2. If the focused area of `candidate` is a browsing context
+ // > container with a non-null nested browsing context, then set
+ // > `candidate` to the active document of that browsing context
+ // > container's nested browsing context.
+ // >
+ // > 3.3. Otherwise, return false.
+ if self.window().parent_info().is_none() {
+ // 2 → 3 → (3.1 || ⋯ → 3.3)
+ self.is_fully_active()
+ } else {
+ // 2 → 3 → 3.2 → (⋯ → 3.1 || ⋯ → 3.3)
+ self.is_fully_active() && self.has_focus.get()
}
- // TODO Step 3.
- false
}
// https://html.spec.whatwg.org/multipage/#dom-document-domain
@@ -6397,6 +6629,17 @@ pub(crate) enum FocusType {
Parent, // Focusing a parent element (an iframe)
}
+/// Specifies the initiator of a focus operation.
+#[derive(Clone, Copy, PartialEq)]
+pub enum FocusInitiator {
+ /// The operation is initiated by this document and to be broadcasted
+ /// through the constellation.
+ Local,
+ /// The operation is initiated somewhere else, and we are updating our
+ /// internal state accordingly.
+ Remote,
+}
+
/// Focus events
pub(crate) enum FocusEventType {
Focus, // Element gained focus. Doesn't bubble.
diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs
index b3345b90fc0..efa9a9a97ab 100644
--- a/components/script/dom/globalscope.rs
+++ b/components/script/dom/globalscope.rs
@@ -457,8 +457,9 @@ pub(crate) struct ManagedMessagePort {
/// and only add them, and ask the constellation to complete the transfer,
/// in a subsequent task if the port hasn't been re-transfered.
pending: bool,
- /// Has the port been closed? If closed, it can be dropped and later GC'ed.
- closed: bool,
+ /// Whether the port has been closed by script in this global,
+ /// so it can be removed.
+ explicitly_closed: bool,
/// Note: it may seem strange to use a pair of options, versus for example an enum.
/// But it looks like tranform streams will require both of those in their transfer.
/// This will be resolved when we reach that point of the implementation.
@@ -546,12 +547,17 @@ impl MessageListener {
let mut succeeded = vec![];
let mut failed = HashMap::new();
- for (id, buffer) in ports.into_iter() {
+ for (id, info) in ports.into_iter() {
if global.is_managing_port(&id) {
succeeded.push(id);
- global.complete_port_transfer(id, buffer);
+ global.complete_port_transfer(
+ id,
+ info.port_message_queue,
+ info.disentangled,
+ CanGc::note()
+ );
} else {
- failed.insert(id, buffer);
+ failed.insert(id, info);
}
}
let _ = global.script_to_constellation_chan().send(
@@ -560,13 +566,21 @@ impl MessageListener {
})
);
},
- MessagePortMsg::CompletePendingTransfer(port_id, buffer) => {
+ MessagePortMsg::CompletePendingTransfer(port_id, info) => {
let context = self.context.clone();
self.task_source.queue(task!(complete_pending: move || {
let global = context.root();
- global.complete_port_transfer(port_id, buffer);
+ global.complete_port_transfer(port_id, info.port_message_queue, info.disentangled, CanGc::note());
}));
},
+ MessagePortMsg::CompleteDisentanglement(port_id) => {
+ let context = self.context.clone();
+ self.task_source
+ .queue(task!(try_complete_disentanglement: move || {
+ let global = context.root();
+ global.try_complete_disentanglement(port_id, CanGc::note());
+ }));
+ },
MessagePortMsg::NewTask(port_id, task) => {
let context = self.context.clone();
self.task_source.queue(task!(process_new_task: move || {
@@ -574,14 +588,6 @@ impl MessageListener {
global.route_task_to_port(port_id, task, CanGc::note());
}));
},
- MessagePortMsg::RemoveMessagePort(port_id) => {
- let context = self.context.clone();
- self.task_source
- .queue(task!(process_remove_message_port: move || {
- let global = context.root();
- global.note_entangled_port_removed(&port_id);
- }));
- },
}
}
}
@@ -871,7 +877,13 @@ impl GlobalScope {
}
/// Complete the transfer of a message-port.
- fn complete_port_transfer(&self, port_id: MessagePortId, tasks: VecDeque<PortMessageTask>) {
+ fn complete_port_transfer(
+ &self,
+ port_id: MessagePortId,
+ tasks: VecDeque<PortMessageTask>,
+ disentangled: bool,
+ can_gc: CanGc,
+ ) {
let should_start = if let MessagePortState::Managed(_id, message_ports) =
&mut *self.message_port_state.borrow_mut()
{
@@ -885,6 +897,10 @@ impl GlobalScope {
}
if let Some(port_impl) = managed_port.port_impl.as_mut() {
port_impl.complete_transfer(tasks);
+ if disentangled {
+ port_impl.disentangle();
+ managed_port.dom_port.disentangle();
+ }
port_impl.enabled()
} else {
panic!("managed-port has no port-impl.");
@@ -895,7 +911,45 @@ impl GlobalScope {
panic!("complete_port_transfer called for an unknown port.");
};
if should_start {
- self.start_message_port(&port_id);
+ self.start_message_port(&port_id, can_gc);
+ }
+ }
+
+ /// The closing of `otherPort`, if it is in a different global.
+ /// <https://html.spec.whatwg.org/multipage/#disentangle>
+ fn try_complete_disentanglement(&self, port_id: MessagePortId, can_gc: CanGc) {
+ let dom_port = if let MessagePortState::Managed(_id, message_ports) =
+ &mut *self.message_port_state.borrow_mut()
+ {
+ let dom_port = if let Some(managed_port) = message_ports.get_mut(&port_id) {
+ if managed_port.pending {
+ unreachable!("CompleteDisentanglement msg received for a pending port.");
+ }
+ let port_impl = managed_port
+ .port_impl
+ .as_mut()
+ .expect("managed-port has no port-impl.");
+ port_impl.disentangle();
+ managed_port.dom_port.as_rooted()
+ } else {
+ // Note: this, and the other return below,
+ // can happen if the port has already been transferred out of this global,
+ // in which case the disentanglement will complete along with the transfer.
+ return;
+ };
+ dom_port
+ } else {
+ return;
+ };
+
+ // Fire an event named close at otherPort.
+ dom_port.upcast().fire_event(atom!("close"), can_gc);
+
+ let res = self.script_to_constellation_chan().send(
+ ScriptToConstellationMessage::DisentanglePorts(port_id, None),
+ );
+ if res.is_err() {
+ warn!("Sending DisentanglePorts failed");
}
}
@@ -951,8 +1005,64 @@ impl GlobalScope {
}
/// <https://html.spec.whatwg.org/multipage/#disentangle>
- pub(crate) fn disentangle_port(&self, _port: &MessagePort) {
- // TODO: #36465
+ pub(crate) fn disentangle_port(&self, port: &MessagePort, can_gc: CanGc) {
+ let initiator_port = port.message_port_id();
+ // Let otherPort be the MessagePort which initiatorPort was entangled with.
+ let Some(other_port) = port.disentangle() else {
+ // Assert: otherPort exists.
+ // Note: ignoring the assert,
+ // because the streams spec seems to disentangle ports that are disentangled already.
+ return;
+ };
+
+ // Disentangle initiatorPort and otherPort, so that they are no longer entangled or associated with each other.
+ // Note: this is done in part here, and in part at the constellation(if otherPort is in another global).
+ let dom_port = if let MessagePortState::Managed(_id, message_ports) =
+ &mut *self.message_port_state.borrow_mut()
+ {
+ let mut dom_port = None;
+ for port_id in &[initiator_port, &other_port] {
+ match message_ports.get_mut(port_id) {
+ None => {
+ continue;
+ },
+ Some(managed_port) => {
+ let port_impl = managed_port
+ .port_impl
+ .as_mut()
+ .expect("managed-port has no port-impl.");
+ managed_port.dom_port.disentangle();
+ port_impl.disentangle();
+
+ if **port_id == other_port {
+ dom_port = Some(managed_port.dom_port.as_rooted())
+ }
+ },
+ }
+ }
+ dom_port
+ } else {
+ panic!("disentangle_port called on a global not managing any ports.");
+ };
+
+ // Fire an event named close at `otherPort`.
+ // Note: done here if the port is managed by the same global as `initialPort`.
+ if let Some(dom_port) = dom_port {
+ dom_port.upcast().fire_event(atom!("close"), can_gc);
+ }
+
+ let chan = self.script_to_constellation_chan().clone();
+ let initiator_port = *initiator_port;
+ self.task_manager()
+ .port_message_queue()
+ .queue(task!(post_message: move || {
+ // Note: we do this in a task to ensure it doesn't affect messages that are still to be routed,
+ // see the task queueing in `post_messageport_msg`.
+ let res = chan.send(ScriptToConstellationMessage::DisentanglePorts(initiator_port, Some(other_port)));
+ if res.is_err() {
+ warn!("Sending DisentanglePorts failed");
+ }
+ }));
}
/// <https://html.spec.whatwg.org/multipage/#entangle>
@@ -984,18 +1094,6 @@ impl GlobalScope {
.send(ScriptToConstellationMessage::EntanglePorts(port1, port2));
}
- /// Note that the entangled port of `port_id` has been removed in another global.
- pub(crate) fn note_entangled_port_removed(&self, port_id: &MessagePortId) {
- // Note: currently this is a no-op,
- // as we only use the `close` method to manage the local lifecyle of a port.
- // This could be used as part of lifecyle management to determine a port can be GC'ed.
- // See https://github.com/servo/servo/issues/25772
- warn!(
- "Entangled port of {:?} has been removed in another global",
- port_id
- );
- }
-
/// Handle the transfer of a port in the current task.
pub(crate) fn mark_port_as_transferred(&self, port_id: &MessagePortId) -> MessagePortImpl {
if let MessagePortState::Managed(_id, message_ports) =
@@ -1021,20 +1119,21 @@ impl GlobalScope {
}
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-start>
- pub(crate) fn start_message_port(&self, port_id: &MessagePortId) {
- let message_buffer = if let MessagePortState::Managed(_id, message_ports) =
+ pub(crate) fn start_message_port(&self, port_id: &MessagePortId, can_gc: CanGc) {
+ let (message_buffer, dom_port) = if let MessagePortState::Managed(_id, message_ports) =
&mut *self.message_port_state.borrow_mut()
{
- match message_ports.get_mut(port_id) {
+ let (message_buffer, dom_port) = match message_ports.get_mut(port_id) {
None => panic!("start_message_port called on a unknown port."),
Some(managed_port) => {
if let Some(port_impl) = managed_port.port_impl.as_mut() {
- port_impl.start()
+ (port_impl.start(), managed_port.dom_port.as_rooted())
} else {
panic!("managed-port has no port-impl.");
}
},
- }
+ };
+ (message_buffer, dom_port)
} else {
return warn!("start_message_port called on a global not managing any ports.");
};
@@ -1042,6 +1141,18 @@ impl GlobalScope {
for task in message_buffer {
self.route_task_to_port(*port_id, task, CanGc::note());
}
+ if dom_port.disentangled() {
+ // <https://html.spec.whatwg.org/multipage/#disentangle>
+ // Fire an event named close at otherPort.
+ dom_port.upcast().fire_event(atom!("close"), can_gc);
+
+ let res = self.script_to_constellation_chan().send(
+ ScriptToConstellationMessage::DisentanglePorts(*port_id, None),
+ );
+ if res.is_err() {
+ warn!("Sending DisentanglePorts failed");
+ }
+ }
}
}
@@ -1055,7 +1166,7 @@ impl GlobalScope {
Some(managed_port) => {
if let Some(port_impl) = managed_port.port_impl.as_mut() {
port_impl.close();
- managed_port.closed = true;
+ managed_port.explicitly_closed = true;
} else {
panic!("managed-port has no port-impl.");
}
@@ -1436,12 +1547,7 @@ impl GlobalScope {
let to_be_removed: Vec<MessagePortId> = message_ports
.iter()
.filter_map(|(id, managed_port)| {
- if managed_port.closed {
- // Let the constellation know to drop this port and the one it is entangled with,
- // and to forward this message to the script-process where the entangled is found.
- let _ = self
- .script_to_constellation_chan()
- .send(ScriptToConstellationMessage::RemoveMessagePort(*id));
+ if managed_port.explicitly_closed {
Some(*id)
} else {
None
@@ -1451,6 +1557,9 @@ impl GlobalScope {
for id in to_be_removed {
message_ports.remove(&id);
}
+ // Note: ports are only removed throught explicit closure by script in this global.
+ // TODO: #25772
+ // TODO: remove ports when we can be sure their port message queue is empty(via the constellation).
message_ports.is_empty()
} else {
false
@@ -1581,7 +1690,7 @@ impl GlobalScope {
port_impl: Some(port_impl),
dom_port: Dom::from_ref(dom_port),
pending: true,
- closed: false,
+ explicitly_closed: false,
cross_realm_transform_readable: None,
cross_realm_transform_writable: None,
},
@@ -1605,7 +1714,7 @@ impl GlobalScope {
port_impl: Some(port_impl),
dom_port: Dom::from_ref(dom_port),
pending: false,
- closed: false,
+ explicitly_closed: false,
cross_realm_transform_readable: None,
cross_realm_transform_writable: None,
},
diff --git a/components/script/dom/htmldetailselement.rs b/components/script/dom/htmldetailselement.rs
index a3e2a05af32..1d48b8e7a97 100644
--- a/components/script/dom/htmldetailselement.rs
+++ b/components/script/dom/htmldetailselement.rs
@@ -178,8 +178,6 @@ impl HTMLDetailsElement {
}
}
shadow_tree.descendants.Assign(slottable_children);
-
- self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
}
fn update_shadow_tree_styles(&self, can_gc: CanGc) {
@@ -214,8 +212,6 @@ impl HTMLDetailsElement {
.implicit_summary
.upcast::<Element>()
.set_string_attribute(&local_name!("style"), implicit_summary_style.into(), can_gc);
-
- self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
}
}
diff --git a/components/script/dom/htmlelement.rs b/components/script/dom/htmlelement.rs
index 9505d5182c7..e7efbde9b1d 100644
--- a/components/script/dom/htmlelement.rs
+++ b/components/script/dom/htmlelement.rs
@@ -32,7 +32,7 @@ use crate::dom::bindings::str::DOMString;
use crate::dom::characterdata::CharacterData;
use crate::dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration, CSSStyleOwner};
use crate::dom::customelementregistry::CallbackReaction;
-use crate::dom::document::{Document, FocusType};
+use crate::dom::document::{Document, FocusInitiator};
use crate::dom::documentfragment::DocumentFragment;
use crate::dom::domstringmap::DOMStringMap;
use crate::dom::element::{AttributeMutation, Element};
@@ -415,18 +415,19 @@ impl HTMLElementMethods<crate::DomTypeHolder> for HTMLElement {
// TODO: Mark the element as locked for focus and run the focusing steps.
// https://html.spec.whatwg.org/multipage/#focusing-steps
let document = self.owner_document();
- document.request_focus(Some(self.upcast()), FocusType::Element, can_gc);
+ document.request_focus(Some(self.upcast()), FocusInitiator::Local, can_gc);
}
// https://html.spec.whatwg.org/multipage/#dom-blur
fn Blur(&self, can_gc: CanGc) {
- // TODO: Run the unfocusing steps.
+ // TODO: Run the unfocusing steps. Focus the top-level document, not
+ // the current document.
if !self.as_element().focus_state() {
return;
}
// https://html.spec.whatwg.org/multipage/#unfocusing-steps
let document = self.owner_document();
- document.request_focus(None, FocusType::Element, can_gc);
+ document.request_focus(None, FocusInitiator::Local, can_gc);
}
// https://drafts.csswg.org/cssom-view/#dom-htmlelement-offsetparent
diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs
index ce6dcca66f3..2421b683bf7 100644
--- a/components/script/dom/htmlformelement.rs
+++ b/components/script/dom/htmlformelement.rs
@@ -1270,8 +1270,14 @@ impl HTMLFormElement {
return;
}
- let controls = self.controls.borrow();
- for child in controls.iter() {
+ let controls: Vec<_> = self
+ .controls
+ .borrow()
+ .iter()
+ .map(|c| c.as_rooted())
+ .collect();
+
+ for child in controls {
let child = child.upcast::<Node>();
match child.type_id() {
diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs
index 9452dcb17a6..58853f600d2 100644
--- a/components/script/dom/htmlscriptelement.rs
+++ b/components/script/dom/htmlscriptelement.rs
@@ -1008,12 +1008,12 @@ impl HTMLScriptElement {
Ok(script) => script,
};
- // TODO: we need to handle this for worker
if let Some(chan) = self.global().devtools_chan() {
let pipeline_id = self.global().pipeline_id();
let source_info = SourceInfo {
url: script.url.clone(),
external: script.external,
+ worker_id: None,
};
let _ = chan.send(ScriptToDevtoolsControlMsg::ScriptSourceLoaded(
pipeline_id,
diff --git a/components/script/dom/messageport.rs b/components/script/dom/messageport.rs
index 85d94c1aa7a..d70d3139b96 100644
--- a/components/script/dom/messageport.rs
+++ b/components/script/dom/messageport.rs
@@ -83,6 +83,20 @@ impl MessagePort {
*self.entangled_port.borrow_mut() = Some(other_id);
}
+ /// <https://html.spec.whatwg.org/multipage/#disentangle>
+ pub(crate) fn disentangle(&self) -> Option<MessagePortId> {
+ // Disentangle initiatorPort and otherPort, so that they are no longer entangled or associated with each other.
+ // Note: called from `disentangle_port` in the global, where the rest happens.
+ self.entangled_port.borrow_mut().take()
+ }
+
+ /// Has the port been disentangled?
+ /// Used when starting the port to fire the `close` event,
+ /// to cover the case of a disentanglement while in transfer.
+ pub(crate) fn disentangled(&self) -> bool {
+ self.entangled_port.borrow().is_none()
+ }
+
pub(crate) fn message_port_id(&self) -> &MessagePortId {
&self.message_port_id
}
@@ -314,20 +328,28 @@ impl MessagePortMethods<crate::DomTypeHolder> for MessagePort {
}
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-start>
- fn Start(&self) {
+ fn Start(&self, can_gc: CanGc) {
if self.detached.get() {
return;
}
- self.global().start_message_port(self.message_port_id());
+ self.global()
+ .start_message_port(self.message_port_id(), can_gc);
}
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-close>
- fn Close(&self) {
+ fn Close(&self, can_gc: CanGc) {
if self.detached.get() {
return;
}
+
+ // Set this's [[Detached]] internal slot value to true.
self.detached.set(true);
- self.global().close_message_port(self.message_port_id());
+
+ let global = self.global();
+ global.close_message_port(self.message_port_id());
+
+ // If this is entangled, disentangle it.
+ global.disentangle_port(self, can_gc);
}
/// <https://html.spec.whatwg.org/multipage/#handler-messageport-onmessage>
@@ -340,15 +362,19 @@ impl MessagePortMethods<crate::DomTypeHolder> for MessagePort {
}
/// <https://html.spec.whatwg.org/multipage/#handler-messageport-onmessage>
- fn SetOnmessage(&self, listener: Option<Rc<EventHandlerNonNull>>) {
+ fn SetOnmessage(&self, listener: Option<Rc<EventHandlerNonNull>>, can_gc: CanGc) {
if self.detached.get() {
return;
}
self.set_onmessage(listener);
// Note: we cannot use the event_handler macro, due to the need to start the port.
- self.global().start_message_port(self.message_port_id());
+ self.global()
+ .start_message_port(self.message_port_id(), can_gc);
}
// <https://html.spec.whatwg.org/multipage/#handler-messageport-onmessageerror>
event_handler!(messageerror, GetOnmessageerror, SetOnmessageerror);
+
+ // <https://html.spec.whatwg.org/multipage/#handler-messageport-onclose>
+ event_handler!(close, GetOnclose, SetOnclose);
}
diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs
index b56126076da..d312fe88fc5 100644
--- a/components/script/dom/node.rs
+++ b/components/script/dom/node.rs
@@ -1007,24 +1007,25 @@ impl Node {
/// <https://dom.spec.whatwg.org/#dom-childnode-replacewith>
pub(crate) fn replace_with(&self, nodes: Vec<NodeOrString>, can_gc: CanGc) -> ErrorResult {
- // Step 1.
- let parent = if let Some(parent) = self.GetParentNode() {
- parent
- } else {
- // Step 2.
+ // Step 1. Let parent be this’s parent.
+ let Some(parent) = self.GetParentNode() else {
+ // Step 2. If parent is null, then return.
return Ok(());
};
- // Step 3.
+
+ // Step 3. Let viableNextSibling be this’s first following sibling not in nodes; otherwise null.
let viable_next_sibling = first_node_not_in(self.following_siblings(), &nodes);
- // Step 4.
+
+ // Step 4. Let node be the result of converting nodes into a node, given nodes and this’s node document.
let node = self
.owner_doc()
.node_from_nodes_and_strings(nodes, can_gc)?;
+
if self.parent_node == Some(&*parent) {
- // Step 5.
+ // Step 5. If this’s parent is parent, replace this with node within parent.
parent.ReplaceChild(&node, self, can_gc)?;
} else {
- // Step 6.
+ // Step 6. Otherwise, pre-insert node into parent before viableNextSibling.
Node::pre_insert(&node, &parent, viable_next_sibling.as_deref(), can_gc)?;
}
Ok(())
@@ -3172,24 +3173,29 @@ impl NodeMethods<crate::DomTypeHolder> for Node {
/// <https://dom.spec.whatwg.org/#concept-node-replace>
fn ReplaceChild(&self, node: &Node, child: &Node, can_gc: CanGc) -> Fallible<DomRoot<Node>> {
- // Step 1.
+ // Step 1. If parent is not a Document, DocumentFragment, or Element node,
+ // then throw a "HierarchyRequestError" DOMException.
match self.type_id() {
NodeTypeId::Document(_) | NodeTypeId::DocumentFragment(_) | NodeTypeId::Element(..) => {
},
_ => return Err(Error::HierarchyRequest),
}
- // Step 2.
+ // Step 2. If node is a host-including inclusive ancestor of parent,
+ // then throw a "HierarchyRequestError" DOMException.
if node.is_inclusive_ancestor_of(self) {
return Err(Error::HierarchyRequest);
}
- // Step 3.
+ // Step 3. If child’s parent is not parent, then throw a "NotFoundError" DOMException.
if !self.is_parent_of(child) {
return Err(Error::NotFound);
}
- // Step 4-5.
+ // Step 4. If node is not a DocumentFragment, DocumentType, Element, or CharacterData node,
+ // then throw a "HierarchyRequestError" DOMException.
+ // Step 5. If either node is a Text node and parent is a document,
+ // or node is a doctype and parent is not a document, then throw a "HierarchyRequestError" DOMException.
match node.type_id() {
NodeTypeId::CharacterData(CharacterDataTypeId::Text(_)) if self.is::<Document>() => {
return Err(Error::HierarchyRequest);
@@ -3201,7 +3207,8 @@ impl NodeMethods<crate::DomTypeHolder> for Node {
_ => (),
}
- // Step 6.
+ // Step 6. If parent is a document, and any of the statements below, switched on the interface node implements,
+ // are true, then throw a "HierarchyRequestError" DOMException.
if self.is::<Document>() {
match node.type_id() {
// Step 6.1
@@ -3255,7 +3262,8 @@ impl NodeMethods<crate::DomTypeHolder> for Node {
}
}
- // Step 7-8.
+ // Step 7. Let referenceChild be child’s next sibling.
+ // Step 8. If referenceChild is node, then set referenceChild to node’s next sibling.
let child_next_sibling = child.GetNextSibling();
let node_next_sibling = node.GetNextSibling();
let reference_child = if child_next_sibling.as_deref() == Some(node) {
@@ -3264,7 +3272,7 @@ impl NodeMethods<crate::DomTypeHolder> for Node {
child_next_sibling.as_deref()
};
- // Step 9.
+ // Step 9. Let previousSibling be child’s previous sibling.
let previous_sibling = child.GetPreviousSibling();
// NOTE: All existing browsers assume that adoption is performed here, which does not follow the DOM spec.
@@ -3285,7 +3293,7 @@ impl NodeMethods<crate::DomTypeHolder> for Node {
None
};
- // Step 12.
+ // Step 12. Let nodes be node’s children if node is a DocumentFragment node; otherwise « node ».
rooted_vec!(let mut nodes);
let nodes = if node.type_id() ==
NodeTypeId::DocumentFragment(DocumentFragmentTypeId::DocumentFragment) ||
@@ -3297,7 +3305,7 @@ impl NodeMethods<crate::DomTypeHolder> for Node {
from_ref(&node)
};
- // Step 13.
+ // Step 13. Insert node into parent before referenceChild with the suppress observers flag set.
Node::insert(
node,
self,
@@ -3306,13 +3314,15 @@ impl NodeMethods<crate::DomTypeHolder> for Node {
can_gc,
);
- // Step 14.
vtable_for(self).children_changed(&ChildrenMutation::replace(
previous_sibling.as_deref(),
&removed_child,
nodes,
reference_child,
));
+
+ // Step 14. Queue a tree mutation record for parent with nodes, removedNodes,
+ // previousSibling, and referenceChild.
let removed = removed_child.map(|r| [r]);
let mutation = LazyCell::new(|| Mutation::ChildList {
added: Some(nodes),
@@ -3323,7 +3333,7 @@ impl NodeMethods<crate::DomTypeHolder> for Node {
MutationObserver::queue_a_mutation_record(self, mutation);
- // Step 15.
+ // Step 15. Return child.
Ok(DomRoot::from_ref(child))
}
diff --git a/components/script/dom/offscreencanvasrenderingcontext2d.rs b/components/script/dom/offscreencanvasrenderingcontext2d.rs
index 2f9b52640e6..b2d0f3201ca 100644
--- a/components/script/dom/offscreencanvasrenderingcontext2d.rs
+++ b/components/script/dom/offscreencanvasrenderingcontext2d.rs
@@ -65,7 +65,7 @@ impl OffscreenCanvasRenderingContext2D {
}
pub(crate) fn set_canvas_bitmap_dimensions(&self, size: Size2D<u64>) {
- self.context.set_bitmap_dimensions(size.cast());
+ self.context.set_canvas_bitmap_dimensions(size.cast());
}
pub(crate) fn send_canvas_2d_msg(&self, msg: Canvas2dMsg) {
diff --git a/components/script/dom/readablestream.rs b/components/script/dom/readablestream.rs
index 51393ab33ae..4982bfa32e3 100644
--- a/components/script/dom/readablestream.rs
+++ b/components/script/dom/readablestream.rs
@@ -1825,7 +1825,7 @@ impl ReadableStream {
global.note_cross_realm_transform_readable(&cross_realm_transform_readable, port_id);
// Enable port’s port message queue.
- port.Start();
+ port.Start(can_gc);
// Perform ! SetUpReadableStreamDefaultController
controller
@@ -2093,7 +2093,7 @@ impl CrossRealmTransformReadable {
self.controller.close(can_gc);
// Disentangle port.
- global.disentangle_port(port);
+ global.disentangle_port(port, can_gc);
}
// Otherwise, if type is "error",
@@ -2102,7 +2102,7 @@ impl CrossRealmTransformReadable {
self.controller.error(value.handle(), can_gc);
// Disentangle port.
- global.disentangle_port(port);
+ global.disentangle_port(port, can_gc);
}
}
@@ -2129,7 +2129,7 @@ impl CrossRealmTransformReadable {
self.controller.error(rooted_error.handle(), can_gc);
// Disentangle port.
- global.disentangle_port(port);
+ global.disentangle_port(port, can_gc);
}
}
diff --git a/components/script/dom/underlyingsourcecontainer.rs b/components/script/dom/underlyingsourcecontainer.rs
index 541a831693a..4acb58bafef 100644
--- a/components/script/dom/underlyingsourcecontainer.rs
+++ b/components/script/dom/underlyingsourcecontainer.rs
@@ -151,7 +151,7 @@ impl UnderlyingSourceContainer {
let result = port.pack_and_post_message_handling_error("error", reason, can_gc);
// Disentangle port.
- self.global().disentangle_port(port);
+ self.global().disentangle_port(port, can_gc);
let promise = Promise::new(&self.global(), can_gc);
diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs
index e210476a5df..a685bbb25f2 100644
--- a/components/script/dom/window.rs
+++ b/components/script/dom/window.rs
@@ -787,6 +787,32 @@ impl WindowMethods<crate::DomTypeHolder> for Window {
doc.abort(can_gc);
}
+ /// <https://html.spec.whatwg.org/multipage/#dom-window-focus>
+ fn Focus(&self) {
+ // > 1. Let `current` be this `Window` object's browsing context.
+ // >
+ // > 2. If `current` is null, then return.
+ let current = match self.undiscarded_window_proxy() {
+ Some(proxy) => proxy,
+ None => return,
+ };
+
+ // > 3. Run the focusing steps with `current`.
+ current.focus();
+
+ // > 4. If current is a top-level browsing context, user agents are
+ // > encouraged to trigger some sort of notification to indicate to
+ // > the user that the page is attempting to gain focus.
+ //
+ // TODO: Step 4
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-window-blur
+ fn Blur(&self) {
+ // > User agents are encouraged to ignore calls to this `blur()` method
+ // > entirely.
+ }
+
// https://html.spec.whatwg.org/multipage/#dom-open
fn Open(
&self,
diff --git a/components/script/dom/windowproxy.rs b/components/script/dom/windowproxy.rs
index e3fc81bf7ec..dc02f9feb49 100644
--- a/components/script/dom/windowproxy.rs
+++ b/components/script/dom/windowproxy.rs
@@ -620,6 +620,23 @@ impl WindowProxy {
result
}
+ /// Run [the focusing steps] with this browsing context.
+ ///
+ /// [the focusing steps]: https://html.spec.whatwg.org/multipage/#focusing-steps
+ pub fn focus(&self) {
+ debug!(
+ "Requesting the constellation to initiate a focus operation for \
+ browsing context {}",
+ self.browsing_context_id()
+ );
+ self.global()
+ .script_to_constellation_chan()
+ .send(ScriptToConstellationMessage::FocusRemoteDocument(
+ self.browsing_context_id(),
+ ))
+ .unwrap();
+ }
+
#[allow(unsafe_code)]
/// Change the Window that this WindowProxy resolves to.
// TODO: support setting the window proxy to a dummy value,
diff --git a/components/script/dom/worker.rs b/components/script/dom/worker.rs
index 429234c7a8e..6ad56026db6 100644
--- a/components/script/dom/worker.rs
+++ b/components/script/dom/worker.rs
@@ -8,7 +8,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
use constellation_traits::{StructuredSerializedData, WorkerScriptLoadOrigin};
use crossbeam_channel::{Sender, unbounded};
-use devtools_traits::{DevtoolsPageInfo, ScriptToDevtoolsControlMsg, WorkerId};
+use devtools_traits::{DevtoolsPageInfo, ScriptToDevtoolsControlMsg, SourceInfo, WorkerId};
use dom_struct::dom_struct;
use ipc_channel::ipc;
use js::jsapi::{Heap, JSObject};
@@ -213,6 +213,16 @@ impl WorkerMethods<crate::DomTypeHolder> for Worker {
devtools_sender.clone(),
page_info,
));
+
+ let source_info = SourceInfo {
+ url: worker_url.clone(),
+ external: true, // Worker scripts are always external.
+ worker_id: Some(worker_id),
+ };
+ let _ = chan.send(ScriptToDevtoolsControlMsg::ScriptSourceLoaded(
+ pipeline_id,
+ source_info,
+ ));
}
}
diff --git a/components/script/dom/writablestream.rs b/components/script/dom/writablestream.rs
index 8c2b2434cd2..1b029f592de 100644
--- a/components/script/dom/writablestream.rs
+++ b/components/script/dom/writablestream.rs
@@ -893,7 +893,7 @@ impl WritableStream {
global.note_cross_realm_transform_writable(&cross_realm_transform_writable, port_id);
// Enable port’s port message queue.
- port.Start();
+ port.Start(can_gc);
// Perform ! SetUpWritableStreamDefaultController
controller
@@ -1202,7 +1202,7 @@ impl CrossRealmTransformWritable {
.error_if_needed(cx, rooted_error.handle(), global, can_gc);
// Disentangle port.
- global.disentangle_port(port);
+ global.disentangle_port(port, can_gc);
}
}
diff --git a/components/script/dom/writablestreamdefaultcontroller.rs b/components/script/dom/writablestreamdefaultcontroller.rs
index 301404ffdb2..084165a6892 100644
--- a/components/script/dom/writablestreamdefaultcontroller.rs
+++ b/components/script/dom/writablestreamdefaultcontroller.rs
@@ -173,11 +173,11 @@ impl Callback for TransferBackPressurePromiseReaction {
self.port
.pack_and_post_message_handling_error("chunk", chunk.handle(), can_gc);
- // Disentangle port.
- global.disentangle_port(&self.port);
-
// If result is an abrupt completion,
if let Err(error) = result {
+ // Disentangle port.
+ global.disentangle_port(&self.port, can_gc);
+
// Return a promise rejected with result.[[Value]].
self.result_promise.reject_error(error, can_gc);
} else {
@@ -609,7 +609,7 @@ impl WritableStreamDefaultController {
let result = port.pack_and_post_message_handling_error("error", reason, can_gc);
// Disentangle port.
- global.disentangle_port(port);
+ global.disentangle_port(port, can_gc);
let promise = Promise::new(global, can_gc);
@@ -752,7 +752,7 @@ impl WritableStreamDefaultController {
.expect("Sending close should not fail.");
// Disentangle port.
- global.disentangle_port(port);
+ global.disentangle_port(port, can_gc);
// Return a promise resolved with undefined.
Promise::new_resolved(global, cx, (), can_gc)