public static bool dispatch_event_to_target(Event @event, EventTarget target, bool legacyTargetOverrideFlag = false, bool legacyOutputDidListenersThrowFlag = false) {/* Docs: https://dom.spec.whatwg.org/#dispatching-events */ /* To dispatch an event to a target, with an optional legacy target override flag and an optional legacyOutputDidListenersThrowFlag, run these steps: */ /* 1) Set event’s dispatch flag. */ @event.Flags |= EEventFlags.Dispatch; /* 2) Let targetOverride be target, if legacy target override flag is not given, and target’s associated Document otherwise. [HTML] */ EventTarget targetOverride = legacyTargetOverrideFlag && target is Node ? (target as Node).ownerDocument : target; EventTarget activationTarget = null; bool clearTargets = false; /* 4) Let relatedTarget be the result of retargeting event’s relatedTarget against target. */ EventTarget relatedTarget = EventCommon.retarget_event(@event.relatedTarget, target); /* 5) If target is not relatedTarget or target is event’s relatedTarget, then: */ if (!ReferenceEquals(target, relatedTarget) || ReferenceEquals(target, @event.relatedTarget)) { var touchTargets = new LinkedList <IEventTarget>(); foreach (var touchTarget in @event.TouchTargetList) { touchTargets.AddLast(EventCommon.retarget_event(touchTarget, target)); } /* 3) Append to an event path with event, target, targetOverride, relatedTarget, touchTargets, and false. */ append_to_event_path(@event, target, targetOverride, relatedTarget, touchTargets, false); /* 4) Let isActivationEvent be true, if event is a MouseEvent object and event’s type attribute is "click", and false otherwise. */ bool isActivationEvent = (@event is MouseEvent mouseEvent && mouseEvent.type == EEventName.Click); /* 5) If isActivationEvent is true and target has activation behavior, then set activationTarget to target. */ if (isActivationEvent && target.has_activation_behaviour) { activationTarget = target; } /* 6) Let slotable be target, if target is a slotable and is assigned, and null otherwise. */ var slotable = (target is Node targetNode && targetNode is ISlottable) ? target : null; bool slotInClosedTree = false; EventTarget parent = target.get_the_parent(@event); while (parent is object) { /* 1) If slotable is non-null: */ if (slotable is object) { /* 1) Assert: parent is a slot. */ if (parent is ISlot) { /* 2) Set slotable to null. */ slotable = null; /* 3) If parent’s root is a shadow root whose mode is "closed", then set slot-in-closed-tree to true. */ if (((Node)parent).getRootNode() is ShadowRoot parentShadow && parentShadow.Mode == Enums.EShadowRootMode.Closed) { slotInClosedTree = true; } } } /* 2) If parent is a slotable and is assigned, then set slotable to parent. */ if (parent is ISlottable && ((ISlottable)parent).isAssigned) { slotable = parent; } /* 3) Let relatedTarget be the result of retargeting event’s relatedTarget against parent. */ relatedTarget = EventCommon.retarget_event(@event.relatedTarget, parent); /* 4) Let touchTargets be a new list. */ touchTargets = new LinkedList <IEventTarget>(); /* 5) For each touchTarget of event’s touch target list, append the result of retargeting touchTarget against parent to touchTargets. */ foreach (var touchTarget in @event.TouchTargetList) { touchTargets.AddLast(EventCommon.retarget_event(touchTarget, parent)); } /* 6) If parent is a Window object, or parent is a node and target’s root is a shadow-including inclusive ancestor of parent, then: */ if (parent is Window || (parent is Node && DOMCommon.Is_Shadow_Including_Inclusive_Ancestor((target as Node).getRootNode(), parent as Node))) { /* 1) If isActivationEvent is true, event’s bubbles attribute is true, activationTarget is null, and parent has activation behavior, then set activationTarget to parent. */ if (isActivationEvent && @event.bubbles && activationTarget == null && parent.has_activation_behaviour) { activationTarget = parent; } /* 2) Append to an event path with event, parent, null, relatedTarget, touchTargets, and slot-in-closed-tree. */ EventCommon.append_to_event_path(@event, parent, null, relatedTarget, touchTargets, slotInClosedTree); } /* 7) Otherwise, if parent is relatedTarget, then set parent to null. */ else if (ReferenceEquals(parent, relatedTarget)) { parent = null; } /* 8) Otherwise, set target to parent and then: */ else { target = parent; /* 1) If isActivationEvent is true, activationTarget is null, and target has activation behavior, then set activationTarget to target. */ if (isActivationEvent && activationTarget == null && target.has_activation_behaviour) { activationTarget = target; } /* 2) Append to an event path with event, parent, target, relatedTarget, touchTargets, and slot-in-closed-tree. */ EventCommon.append_to_event_path(@event, parent, target, relatedTarget, touchTargets, slotInClosedTree); } /* 9) If parent is non-null, then set parent to the result of invoking parent’s get the parent with event. */ if (parent is object) { parent = parent.get_the_parent(@event); } /* 10) Set slot-in-closed-tree to false. */ slotInClosedTree = false; } /* 10) Let clearTargetsStruct be the last struct in event’s path whose shadow-adjusted target is non-null. */ var clearTargetsStruct = @event.Path.FindLast(p => null != p.shadow_adjusted_target); /* 11) Let clearTargets be true if clearTargetsStruct’s shadow-adjusted target, clearTargetsStruct’s relatedTarget, or an EventTarget object in clearTargetsStruct’s touch target list is a node and its root is a shadow root, and false otherwise. */ clearTargets = (clearTargetsStruct.shadow_adjusted_target is Node n1 && n1.getRootNode() is ShadowRoot) || (clearTargetsStruct.relatedTarget is Node n2 && n2.getRootNode() is ShadowRoot) || clearTargetsStruct.touch_target_list.Any(t => t is Node n3 && n3.getRootNode() is ShadowRoot); /* 12) If activationTarget is non-null and activationTarget has legacy-pre-activation behavior, then run activationTarget’s legacy-pre-activation behavior. */ if (activationTarget is object && activationTarget.has_legacy_activation_behaviour) { activationTarget?.legacy_pre_activation_behaviour(); } /* 13) For each struct in event’s path, in reverse order: */ for (int i = @event.Path.Count - 1; i >= 0; i--) { EventPathItem @struct = @event.Path[i]; /* 1) If struct’s shadow-adjusted target is non-null, then set event’s eventPhase attribute to AT_TARGET. */ if (@struct.shadow_adjusted_target is object) { @event.eventPhase = EEventPhase.AT_TARGET; } /* 2) Otherwise, set event’s eventPhase attribute to CAPTURING_PHASE. */ else { @event.eventPhase = EEventPhase.CAPTURING_PHASE; } /* 3) Invoke with struct, event, "capturing", and legacyOutputDidListenersThrowFlag if given. */ EventCommon.invoke(@struct, @event, EEventPhase.CAPTURING_PHASE, legacyOutputDidListenersThrowFlag); } /* 14) For each struct in event’s path: */ foreach (EventPathItem @struct in @event.Path) { /* 1) If struct’s shadow-adjusted target is non-null, then set event’s eventPhase attribute to AT_TARGET. */ if (@struct.shadow_adjusted_target is object) { @event.eventPhase = EEventPhase.AT_TARGET; } /* 2) Otherwise: */ else { if ([email protected]) { continue; } @event.eventPhase = EEventPhase.BUBBLING_PHASE; } EventCommon.invoke(@struct, @event, EEventPhase.BUBBLING_PHASE, legacyOutputDidListenersThrowFlag); } } /* 6) Set event’s eventPhase attribute to NONE. */ @event.eventPhase = EEventPhase.NONE; /* 7) Set event’s currentTarget attribute to null. */ @event.currentTarget = null; /* 8) Set event’s path to the empty list. */ @event.Path.Clear(); /* 9) Unset event’s dispatch flag, stop propagation flag, and stop immediate propagation flag. */ @event.Flags &= ~(EEventFlags.Dispatch | EEventFlags.StopPropogation | EEventFlags.StopImmediatePropogation); /* 10) If clearTargets, then: */ if (clearTargets) { @event.target = null; @event.relatedTarget = null; @event.TouchTargetList.Clear(); } /* 11) If activationTarget is non-null, then: */ if (activationTarget is object) { /* 1) If event’s canceled flag is unset, then run activationTarget’s activation behavior with event. */ if (0 == (@event.Flags & EEventFlags.Canceled)) { activationTarget.activation_behaviour(@event); } /* 2) Otherwise, if activationTarget has legacy-canceled-activation behavior, then run activationTarget’s legacy-canceled-activation behavior. */ else if (activationTarget.has_legacy_activation_behaviour) { activationTarget.legacy_canceled_pre_activation_behaviour(); } } /* 12) Return false if event’s canceled flag is set, and true otherwise. */ return(0 == (@event.Flags & EEventFlags.Canceled)); }