Example #1
0
        /// <summary>
        /// Appends a new <see cref="EventPathItem"/> to the given events Path
        /// </summary>
        /// <param name="event"></param>
        /// <param name="invocationTarget"></param>
        /// <param name="shadowAdjustedTarget"></param>
        /// <param name="relatedTarget"></param>
        /// <param name="touchTargets"></param>
        /// <param name="slotInClosedTree"></param>
        public static void append_to_event_path(Event @event, EventTarget invocationTarget, EventTarget shadowAdjustedTarget, EventTarget relatedTarget, LinkedList <IEventTarget> touchTargets, bool slotInClosedTree)
        {/* Docs: https://dom.spec.whatwg.org/#concept-event-path-append */
            bool invocationTargetInShadowTree = false;
            bool rootOfClosedTree             = false;

            if (invocationTarget is Node invTargNode)
            {
                if (invTargNode.getRootNode() is ShadowRoot)
                {
                    invocationTargetInShadowTree = true;
                }

                if (invTargNode is ShadowRoot && ((ShadowRoot)invTargNode).Mode == Enums.EShadowRootMode.Closed)
                {
                    rootOfClosedTree = true;
                }
            }


            var newPath = new EventPathItem()
            {
                invocationTarget = invocationTarget,
                invocation_target_in_shadow_tree = invocationTargetInShadowTree,
                shadow_adjusted_target           = shadowAdjustedTarget,
                relatedTarget       = relatedTarget,
                touch_target_list   = touchTargets,
                root_of_closed_tree = rootOfClosedTree,
                slot_in_closed_tree = slotInClosedTree
            };

            @event.Path.Add(newPath);
        }
Example #2
0
        public static void invoke(EventPathItem @struct, Event @event, EEventPhase phase, bool legacyOutputDidListenersThrowFlag = false)
        {/* Docs: https://dom.spec.whatwg.org/#concept-event-listener-invoke */
            /* 1) Set event’s target to the shadow-adjusted target of the last struct in event’s path, that is either struct or preceding struct, whose shadow-adjusted target is non-null. */
            if (@struct.shadow_adjusted_target is object)
            {
                @event.target = @struct.shadow_adjusted_target;
            }
            else
            {
                /* Find the location of struct in events path */
                int structIndex = @event.Path.IndexOf(@struct);
                /* Find the path-item preceeding struct which has a valid shadow-adjusted target */
                for (int i = structIndex - 1; i >= 0; i--)
                {
                    if (@event.Path[i].shadow_adjusted_target is object)
                    {
                        @event.target = @event.Path[i].shadow_adjusted_target;
                        break;
                    }
                }
            }

            @event.relatedTarget   = @struct.relatedTarget;
            @event.TouchTargetList = @struct.touch_target_list;
            /* 4) If event’s stop propagation flag is set, then return. */
            if (0 != (@event.Flags & EEventFlags.StopPropogation))
            {
                return;
            }

            @event.currentTarget = @struct.invocationTarget;
            /* 6) Let listeners be a clone of event’s currentTarget attribute value’s event listener list. */
            var listeners = @event.currentTarget.Listeners.ToArray();
            /* 7) Let found be the result of running inner invoke with event, listeners, phase, and legacyOutputDidListenersThrowFlag if given. */
            bool found = EventCommon.inner_invoke(@event, listeners, phase, legacyOutputDidListenersThrowFlag);

            /* 8) If found is false and event’s isTrusted attribute is true, then: */
            if (!found && @event.isTrusted)
            {
                /* Do nothing, this section of the specifications is just firing the event under it's legacy event name */
            }
        }
Example #3
0
        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));
        }