コード例 #1
0
        internal static void Run_Dialog_Focusing_Steps(Element subject)
        {/* Docs: https://html.spec.whatwg.org/multipage/interactive-elements.html#dialog-focusing-steps */
            if (subject.isInert)
            {
                return;
            }
            /* 2) Let control be the first descendant element of subject, in tree order, that is not inert and has the autofocus attribute specified. */
            Element firstNonInert  = null;
            Element firstAutofocus = null;

            var     tree    = new TreeWalker(subject, ENodeFilterMask.SHOW_ELEMENT, FilterNonInert.Instance);
            Element current = (Element)tree.nextNode();

            while (current is object)
            {
                if (current is Element currentElement)
                {
                    firstNonInert ??= currentElement;
                    if (currentElement.hasAttribute(EAttributeName.Autofocus))
                    {
                        firstAutofocus = currentElement;
                        break;
                    }
                }
            }

            /* If there isn't one, then let control be the first non-inert descendant element of subject, in tree order. */
            /* If there isn't one of those either, then let control be subject. */
            Element control = firstAutofocus ?? firstNonInert ?? subject;

            DOMCommon.Run_Focusing_Steps(control);
        }
コード例 #2
0
ファイル: DOMImplementation.cs プロジェクト: dsisco11/CssUI
        public XMLDocument createDocument(string Namespace, string qualifiedName, DocumentType doctype = null)
        {/* Docs: https://dom.spec.whatwg.org/#dom-domimplementation-createdocument */
            var     document = new XMLDocument(Namespace);
            Element element  = null;

            if (!string.IsNullOrEmpty(qualifiedName))
            {
                element = DOMCommon.createElementNS(document, qualifiedName, Namespace);
            }

            /* 4) If doctype is non-null, append doctype to document. */
            if (doctype != null)
            {
                document.append(doctype);
            }
            /* 5) If element is non-null, append element to document. */
            if (element != null)
            {
                document.append(element);
            }

            /* 6) document’s origin is context object’s associated document’s origin. */

            return(document);
        }
コード例 #3
0
ファイル: DOMImplementation.cs プロジェクト: dsisco11/CssUI
        public Document createHTMLDocument(string title)
        {
            var doc = new HTMLDocument("text/html");

            doc.append(new DocumentType("html")
            {
                nodeDocument = doc
            });
            /* 4) Append the result of creating an element given doc, html, and the HTML namespace, to doc. */
            var html = DOMCommon.createElementNS(doc, "html", DOMCommon.HTMLNamespace);

            doc.append(html);
            /* 5) Append the result of creating an element given doc, head, and the HTML namespace, to the html element created earlier. */
            var head = DOMCommon.createElementNS(doc, "head", DOMCommon.HTMLNamespace);

            html.append(head);
            /* 6) If title is given: */
            if (title != null)
            {
                /* 1) Append the result of creating an element given doc, title, and the HTML namespace, to the head element created earlier. */
                var titleElement = DOMCommon.createElementNS(doc, "title", DOMCommon.HTMLNamespace);
                head.append(titleElement);
                /* 2) Append a new Text node, with its data set to title (which could be the empty string) and its node document set to doc, to the title element created earlier. */
                var textNode = new Text(doc, title);
                titleElement.append(textNode);
            }
            /* 7) Append the result of creating an element given doc, body, and the HTML namespace, to the html element created earlier. */
            var body = DOMCommon.createElementNS(doc, "body", DOMCommon.HTMLNamespace);

            html.append(body);

            return(doc);
        }
コード例 #4
0
ファイル: ElementInternals.cs プロジェクト: dsisco11/CssUI
        /// <summary>
        /// Sets both the state and submission value of internals's target element to value.
        /// If value is null, the element won't participate in form submission.
        /// </summary>
        /// <param name="value">New value to assign to the element, must be <see cref="string"/>, <see cref="FileBlob"/>, or <see cref="FormData"/></param>
        /// <param name="state">New state to give the element, must be <see cref="string"/>, <see cref="FileBlob"/>, or <see cref="FormData"/></param>
        public void setFormValue(FormSubmissionValue value, FormSubmissionValue state = null)
        {/* Docs: https://html.spec.whatwg.org/multipage/custom-elements.html#dom-elementinternals-setformvalue */
            if (!DOMCommon.Is_Form_Associated_Custom_Element(TargetElement))
            {
                throw new NotSupportedError($"Element internals may only function on custom form-associated elements");
            }

            var element = (FormAssociatedElement)TargetElement;

            /* 3) Set target element's submission value to value if value is not a FormData object, or to a clone of the entry list associated with value otherwise. */
            if (value.Type == ESubmissionValue.EntryList)
            {
                element.submission_value = new FormData(value.Value);
            }
            else
            {
                element.submission_value = value;
            }

            /* 4) If the state argument of the function is omitted, set element's state to its submission value. */
            if (state is null)
            {
                element.state = element.submission_value;
            }
            else if (state.Type == ESubmissionValue.EntryList)
            {
                element.state = new FormData(state.Value);
            }
            else
            {
                element.state = state;
            }
        }
コード例 #5
0
ファイル: ElementInternals.cs プロジェクト: dsisco11/CssUI
        /// <summary>
        /// Returns true if internals's target element has no validity problems; otherwise, returns false, fires an invalid event at the element, and (if the event isn't canceled) reports the problem to the user.
        /// </summary>
        /// <returns></returns>
        public bool reportValidity()
        {/* Docs: https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-elementinternals-reportvalidity */
            if (!DOMCommon.Is_Form_Associated_Custom_Element(TargetElement))
            {
                throw new NotSupportedError($"Element internals may only function on custom form-associated elements");
            }

            var element = (FormAssociatedElement)TargetElement;

            return(element.reportValidity());
        }
コード例 #6
0
        /// <summary>
        /// Returns the first element that is a descendant of node that matches selectors.
        /// </summary>
        /// <param name="selectors"></param>
        /// <returns></returns>
        public Element querySelector(string selector)
        {
            var results = DOMCommon.Scope_Match_Selector_String(this, selector);

            if (results.Any())
            {
                return(results.First());
            }

            return(null);
        }
コード例 #7
0
        public override ENodeFilterResult acceptNode(Node node)
        {
            if (node.nodeType != ENodeType.ELEMENT_NODE || !(node is HTMLElement nodeElement))
            {
                return(ENodeFilterResult.FILTER_REJECT);
            }

            if (DOMCommon.Is_Submittable_Element(nodeElement))
            {
                return(ENodeFilterResult.FILTER_ACCEPT);
            }

            return(ENodeFilterResult.FILTER_SKIP);
        }
コード例 #8
0
        internal static bool Is_Being_Used_As_Canvas_Fallback_Content(Element element)
        {/* Docs: https://html.spec.whatwg.org/multipage/canvas.html#being-used-as-relevant-canvas-fallback-content */
            /* An element whose nearest canvas element ancestor is being rendered and represents embedded content is an element that is being used as relevant canvas fallback content. */
            var canvas = DOMCommon.Get_Nth_Ancestor <HTMLCanvasElement>(element, 1, null, ENodeFilterMask.SHOW_ELEMENT);

            if (canvas != null)
            {
                if (canvas.is_being_rendered)
                {
                    return(true);
                }
            }

            return(false);
        }
コード例 #9
0
        public override ENodeFilterResult acceptNode(Node node)
        {
            /* omitting any node without a parent */
            if (ReferenceEquals(node.parentNode, null))
            {
                return(Negative);
            }

            if (DOMCommon.Is_Shadow_Including_Descendant(node, targetNode))
            {
                return(Affirmative);
            }

            return(Negative);
        }
コード例 #10
0
        public override ENodeFilterResult acceptNode(Node node)
        {
            /* omitting any node without a parent */
            if (ReferenceEquals(node.parentNode, null))
            {
                return(ENodeFilterResult.FILTER_REJECT);
            }

            if (DOMCommon.Is_Descendant(node, targetNode))
            {
                return(ENodeFilterResult.FILTER_ACCEPT);
            }

            return(ENodeFilterResult.FILTER_REJECT);
        }
コード例 #11
0
        internal static void Modal_Dialog_Unblock_Document(Document document, HTMLDialogElement dialog)
        {/* Docs: https://html.spec.whatwg.org/multipage/interaction.html#blocked-by-a-modal-dialog */
            /* ...While document is so blocked, every node that is connected to document, with the exception of the subject element and its shadow-including descendants, must be marked inert */
            if (document.topLayer.Contains(dialog))
            {
                document.topLayer.Remove(dialog);
            }

            var Filter      = new FilterShadowIncludingDescendantOf(dialog, ENodeFilterResult.FILTER_REJECT, ENodeFilterResult.FILTER_ACCEPT);
            var descendants = DOMCommon.Get_Shadow_Including_Descendents(document, Filter, ENodeFilterMask.SHOW_ALL);

            foreach (Node descendant in descendants)
            {
                descendant.isInert = false;
            }
        }
コード例 #12
0
        private void dtl_update()
        {
            /* A DOMTokenList object’s update steps are: */
            /* 1) If the associated element does not have an associated attribute and token set is empty, then return. */
            string name = localName.ToString();

            if (!ownerElement.AttributeList.ContainsKey(name) && TokenSet.Count <= 0)
            {
                return;
            }

            /* 2) Set an attribute value for the associated element using associated attribute’s local name and the result of running the ordered set serializer for token set. */
            string serialized = DOMCommon.Serialize_Ordered_Set(TokenSet.Select(tok => (Token_To_String(tok)).AsMemory()));

            ownerElement.setAttribute(localName, AttributeValue.From(serialized));
        }
コード例 #13
0
ファイル: ElementInternals.cs プロジェクト: dsisco11/CssUI
        /// <summary>
        /// Marks internals's target element as suffering from the constraints indicated by the flags argument, and sets the element's validation message to message.
        /// If anchor is specified, the user agent might use it to indicate problems with the constraints of internals's target element when the form owner is validated interactively or reportValidity() is called.
        /// </summary>
        /// <param name="flags"></param>
        /// <param name="message"></param>
        /// <param name="anchor"></param>
        public void setValidity(EValidityState flags, string message, HTMLElement anchor)
        {/* Docs: https://html.spec.whatwg.org/multipage/custom-elements.html#dom-elementinternals-setvalidity */
            if (!DOMCommon.Is_Form_Associated_Custom_Element(TargetElement))
            {
                throw new NotSupportedError($"Element internals may only function on custom form-associated elements");
            }

            if (flags != 0 && (ReferenceEquals(null, message) || message.Length <= 0))
            {
                throw new TypeError("A message must be given when setting validity flags");
            }

            var element = (FormAssociatedElement)TargetElement;

            element.validity          = flags;
            element.validationMessage = ReferenceEquals(null, message) ? string.Empty : message;

            if (0 != (flags & EValidityState.customError))
            {
                element.custom_validity_error_message = element.validationMessage;
            }
            else
            {
                element.custom_validity_error_message = string.Empty;
            }

            if (anchor == null)
            {
                element.validation_anchor = anchor;
            }
            else
            {
                if (!DOMCommon.Is_Shadow_Including_Descendant(anchor, TargetElement))
                {
                    throw new NotFoundError("validity anchor element must be a descendant of the element it is an anchor for");
                }

                element.validation_anchor = anchor;
            }
        }
コード例 #14
0
        /// <summary>
        /// Retargets <paramref name="A"/> against object <paramref name="B"/>
        /// </summary>
        /// <param name="A"></param>
        /// <param name="B"></param>
        /// <returns></returns>
        internal static EventTarget retarget_event(IEventTarget A, IEventTarget B)
        {/* Docs: https://dom.spec.whatwg.org/#retarget */
            while (A is object)
            {
                if (!(A is Node))
                {
                    return((EventTarget)A);
                }
                if (A is Node nA && nA.getRootNode() is ShadowRoot)
                {
                    return((EventTarget)A);
                }
                if (B is Node nB && DOMCommon.Is_Shadow_Including_Inclusive_Ancestor(((Node)A).getRootNode(), (Node)B))
                {
                    return((EventTarget)A);
                }

                A = (((Node)A).getRootNode() as DocumentFragment).Host;
            }

            return(null);
        }
コード例 #15
0
        /// <summary>
        /// Returns the item with ID or name name from the collection.
        /// If there are multiple matching items, then a RadioNodeList object containing all those elements is returned.
        /// </summary>
        public new dynamic this[string name]
        {
            get
            {
                if (ReferenceEquals(null, name) || name.Length <= 0)
                {
                    return(null);
                }

                var matched = DOMCommon.Get_Descendents <HTMLElement>(root, new FilterNamedElement(name), ENodeFilterMask.SHOW_ELEMENT);
                if (matched.Count <= 0)
                {
                    return(null);
                }
                else if (matched.Count == 1)
                {
                    return(matched.First());
                }
                else
                {
                    return(new RadioNodeList(matched.ToArray()));
                }
            }
        }
コード例 #16
0
        public static EventTarget determine_target(EventTarget eventTarget, EventName Name)
        {/* Docs: https://html.spec.whatwg.org/multipage/webappapis.html#determining-the-target-of-an-event-handler */
         /* 1) If eventTarget is not a body element or a frameset element, then return eventTarget. */
            if ((eventTarget is Element element) && !element.tagName.Equals("body") && element.tagName.Equals("frameset"))
            {
                return(eventTarget);
            }

            /* 2) If name is not the name of an attribute member of the WindowEventHandlers interface mixin and the Window-reflecting body element event handler set does not contain name, then return eventTarget. */
            if (!EventCommon.Is_Window_Event(Name.EnumValue) && !EventCommon.Is_Window_Reflecting_Body_Element_Event(Name.EnumValue))
            {
                return(eventTarget);
            }

            /* 3) If eventTarget's node document is not an active document, then return null. */
            if (!DOMCommon.Is_Active_Document((eventTarget as Node).ownerDocument))
            {
                return(null);
            }

            /* 4) Return eventTarget's node document's relevant global object. */
            /* For us this just means the actual document */
            return((eventTarget as Node).ownerDocument);
        }
コード例 #17
0
 /// <summary>
 /// Returns all element descendants of node that match selectors.
 /// </summary>
 /// <param name="selectors"></param>
 /// <returns></returns>
 public IEnumerable <Node> querySelectorAll(string selectors)
 {
     return(DOMCommon.Scope_Match_Selector_String(this, selectors));
 }
コード例 #18
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));
        }
コード例 #19
0
        /// <summary>
        /// Applies the Complex selectors combinator to a single element
        /// </summary>
        /// <param name="element"></param>
        /// <returns></returns>
        public IEnumerable <Element> Apply_Combinator(Element element, ESelectorMatchingOrder Dir)
        {
            switch (Combinator)
            {
            case ESelectorCombinator.Child:
            {
                switch (Dir)
                {
                case ESelectorMatchingOrder.LTR:
                    return(new LinkedList <Element>(element.children));

                default:
                    return(new Element[] { element.parentElement });
                }
            }

            case ESelectorCombinator.Sibling_Adjacent:
            {
                switch (Dir)
                {
                case ESelectorMatchingOrder.LTR:
                    return(new Element[] { element.nextElementSibling });

                default:
                    return(new Element[] { element.previousElementSibling });
                }
            }

            case ESelectorCombinator.Sibling_Subsequent:
            {
                switch (Dir)
                {
                case ESelectorMatchingOrder.LTR:
                    return(DOMCommon.Get_Following <Element>(element, FilterElements.Instance));

                default:
                    return(DOMCommon.Get_Preceeding <Element>(element, FilterElements.Instance));
                }
            }

            case ESelectorCombinator.Descendant:
            {
                switch (Dir)
                {
                case ESelectorMatchingOrder.LTR:
                    return(DOMCommon.Get_Descendents <Element>(element, FilterElements.Instance));

                default:
                    return(DOMCommon.Get_Ancestors <Element>(element, FilterElements.Instance));
                }
            }

            case ESelectorCombinator.None:
            {
                return(new Element[] { element });
            }

            default:
                throw new NotImplementedException($"[CSS][Selector] Unhandled selector-combinator({Enum.GetName(typeof(ESelectorCombinator), Combinator)})!");
            }
        }
コード例 #20
0
        public static bool Is_Containing_Block_Ancestor_Of(Element A, Element B)
        {/* Docs: https://www.w3.org/TR/CSS22/visudet.html#containing-block-details */
            /* Root elements */
            if (A.parentElement is null)
            {
                return(true);
            }

            /* Other elements */
            switch (A.Style.Positioning)
            {
            case EBoxPositioning.Static:
            case EBoxPositioning.Relative:
            {
                /*
                 * For other elements, if the element's position is 'relative' or 'static',
                 * the containing block is formed by the content edge of the nearest ancestor box that is a block container or which establishes a formatting context.
                 */

                var Tree    = new DOM.TreeWalker(A, DOM.Enums.ENodeFilterMask.SHOW_ELEMENT);
                var Current = Tree.parentNode();
                while (Current is object)
                {
                    if (Current is DOM.Element element)
                    {
                        if (element.Box.DisplayType.Outer == EOuterDisplayType.Block || element.Box.FormattingContext is object)
                        {
                            return(DOMCommon.Is_Ancestor(element, B));
                        }
                    }

                    Current = Tree.parentNode();
                }

                throw new CssException($"Cant find containing-block for element: {A.ToString()}");
            }

            case EBoxPositioning.Fixed:
            {        /* If the element has 'position: fixed', the containing block is established by the viewport in the case of continuous media or the page area in the case of paged media. */
                return(true);
            }

            case EBoxPositioning.Absolute:
            {
                /*
                 * If the element has 'position: absolute', the containing block is established by the nearest ancestor with a 'position' of 'absolute', 'relative' or 'fixed', in the following way:
                 * In the case that the ancestor is an inline element, the containing block is the bounding box around the padding boxes of the first and the last inline boxes generated for that element.
                 * In CSS 2.2, if the inline element is split across multiple lines, the containing block is undefined.
                 * Otherwise, the containing block is formed by the padding edge of the ancestor.
                 *
                 */

                var Tree    = new DOM.TreeWalker(A, DOM.Enums.ENodeFilterMask.SHOW_ELEMENT);
                var Current = Tree.parentNode();
                while (Current is object)
                {
                    if (Current is DOM.Element ancestor)
                    {
                        if (ancestor.Style.Positioning == EBoxPositioning.Absolute || ancestor.Style.Positioning == EBoxPositioning.Relative || ancestor.Style.Positioning == EBoxPositioning.Fixed)
                        {
                            return(DOMCommon.Is_Ancestor(ancestor, B));
                        }
                    }

                    Current = Tree.parentNode();
                }

                /* If there is no such ancestor, the containing block is the initial containing block. */
                return(DOMCommon.Is_Ancestor(A.getRootNode(), B));
            }

            default:
            {
                return(DOMCommon.Is_Ancestor(A.parentElement, B));
            }
            }
        }
コード例 #21
0
ファイル: CssSelector.cs プロジェクト: dsisco11/CssUI
        /// <summary>
        /// Performs matching against a single element
        /// </summary>
        /// <param name="E"></param>
        /// <param name="scopeElements"></param>
        /// <param name="scopingRoot">Element to limit the search to, only this element and its descendents will be matched against</param>
        /// <returns>All matched elements</returns>
        public IEnumerable <Element> Match_Against_Tree(IEnumerable <Node> rootElements, Node scopingRoot, params Node[] scopeElements)
        {/* Docs: https://drafts.csswg.org/selectors-4/#match-a-selector-against-a-tree */
            if (!rootElements.Any())
            {
                return(System.Array.Empty <Element>());
            }

            /* "All of the root elements must share the same root, or else calling this algorithm is invalid." */
            Node treeRoot = rootElements.First().getRootNode();

            foreach (var element in rootElements)
            {
                if (!ReferenceEquals(treeRoot, element.getRootNode()))
                {
                    throw new CssSelectorException("When matching a selector against a tree, all rootElements must share the same root!");
                }
            }

            /* Resolve scoping */
            if (!scopeElements.Any())
            {
                if (scopingRoot != null)
                {
                    scopeElements = new Node[] { scopingRoot };
                }
                else
                {
                    scopeElements = new List <Node>(rootElements).ToArray();
                }
            }

            /*
             * This algorithm returns a (possibly empty) list of elements.
             * Start with a list of candidate elements, which are the the root elements and all of their descendant elements, sorted in shadow-including tree order, unless otherwise specified.
             * If an optional scoping root was provided, then remove from the candidate elements any elements that are not descendants of the scoping root.
             * Initialize the selector match list to empty.
             * For each element in the set of candidate elements:
             * If the result of match a selector against an element for element and selector is success, add element to the selector match list.
             * For each possible pseudo-element associated with element that is one of the pseudo-elements allowed to show up in the match list, if the result of match a selector against a pseudo-element for the pseudo-element and selector is success, add the pseudo-element to the selector match list.
             */
            NodeFilter Filter = null;

            if (scopingRoot != null)
            {
                Filter = new FilterDescendantOf(scopingRoot);
            }

            LinkedList <Node> candidateElements = new LinkedList <Node>();

            foreach (Node root in rootElements)
            {
                var rootList = DOMCommon.Get_Shadow_Including_Inclusive_Descendents(root, Filter, ENodeFilterMask.SHOW_ELEMENT);
                LinkedListNode <Node> firstNode = ((LinkedList <Node>)rootList).First;
                candidateElements.AddLast(firstNode);
            }

            LinkedList <Element> matchList = new LinkedList <Element>();

            foreach (Element candidate in candidateElements)
            {
                if (Match(candidate, scopeElements))
                {
                    matchList.AddLast(candidate);
                }
            }
            /* For each possible pseudo-element associated with element that is one of the pseudo-elements allowed to show up in the match list, if the result of match a selector against a pseudo-element for the pseudo-element and selector is success, add the pseudo-element to the selector match list. */
            /* XXX: pseudo-element Need to implement this stuff */

            return(matchList.ToArray());
        }
コード例 #22
0
        /// <summary>
        /// Resolves an elements IDL-exposed autofill value
        /// </summary>
        /// <param name="element"></param>
        /// <param name="outFieldName">The autofill field name specifies the specific kind of data expected in the field, e.g. "street-address" or "cc-exp".</param>
        /// <param name="outHint">The autofill hint set identifies what address or contact information type the user agent is to look at, e.g. "shipping fax" or "billing".</param>
        /// <param name="outScope">The autofill scope identifies the group of fields whose information concerns the same subject, and consists of the autofill hint set with, if applicable, the "section-*" prefix, e.g. "billing", "section-parent shipping", or "section-child shipping home".</param>
        public static void Resolve_Autofill(FormAssociatedElement element, out EAutofill outFieldName, out IReadOnlyList <string> outHint, out IReadOnlyList <string> outScope, out string outidlValue)
        {/* Docs: https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill-processing-model */
            AttributeValue  autocomplete = element.getAttribute(EAttributeName.Autocomplete);
            EAutofillMantle afMantle     = Get_Autofill_Mantle(element);

            if (autocomplete != null)
            {
                /* 2) Let tokens be the result of splitting the attribute's value on ASCII whitespace. */
                //var tokens = DOMCommon.Parse_Ordered_Set(autocomplete.Get_String().AsMemory());
                string acString  = StringCommon.Transform(autocomplete.AsString().AsMemory(), To_ASCII_Lower_Alpha);
                var    tokenList = new List <string>(DOMCommon.Parse_Ordered_Set(acString.AsMemory()).Select(o => o.ToString()));
                tokenList.Reverse();
                DataConsumer <string> Stream = new DataConsumer <string>(tokenList.ToArray(), null);

                /* 3) If tokens is empty, then jump to the step labeled default. */
                if (Stream.Length > 0)
                {
                    /* 4) Let index be the index of the last token in tokens. */
                    //int index = tokens.Count - 1;

                    /* 5) If the indexth token in tokens is not an ASCII case-insensitive match for one of the tokens given in the first column of the following table,
                     * or if the number of tokens in tokens is greater than the maximum number given in the cell in the second column of that token's row, then jump to the step labeled default.
                     * Otherwise, let field be the string given in the cell of the first column of the matching row, and let category be the value of the cell in the third column of that same row. */

                    //var key = tokens[index];
                    string            key        = Stream.Next;
                    EAutofill         afValue    = Lookup.Enum <EAutofill>(key);
                    EnumData          afData     = Lookup.Data(afValue);
                    int               maxTokens  = (int)afData[0];
                    EAutofillCategory afCategory = (EAutofillCategory)afData[1];

                    /* ...if the number of tokens in tokens is greater than the maximum number given in the cell in the second column of that token's row, then jump to the step labeled default. */
                    if (Stream.Length <= maxTokens)
                    {
                        var field    = afValue;
                        var category = afCategory;

                        /* 6) If category is Off or Automatic but the element's autocomplete attribute is wearing the autofill anchor mantle, then jump to the step labeled default. */
                        if ((category == EAutofillCategory.Off || category == EAutofillCategory.Automatic) && afMantle == EAutofillMantle.Anchor)
                        {/* "Jump" to default */
                        }
                        else
                        {
                            /* 7) If category is Off, let the element's autofill field name be the string "off", let its autofill hint set be empty, and let its IDL-exposed autofill value be the string "off". Then, return. */
                            if (category == EAutofillCategory.Off)
                            {
                                outFieldName = EAutofill.Off;
                                outHint      = Array.Empty <string>();
                                outScope     = Array.Empty <string>();
                                outidlValue  = "off";
                                return;
                            }

                            /* 8) If category is Automatic, let the element's autofill field name be the string "on", let its autofill hint set be empty, and let its IDL-exposed autofill value be the string "on". Then, return. */
                            if (category == EAutofillCategory.Automatic)
                            {
                                outFieldName = EAutofill.On;
                                outHint      = Array.Empty <string>();
                                outScope     = Array.Empty <string>();
                                outidlValue  = "on";
                                return;
                            }

                            /* 9) Let scope tokens be an empty list. */
                            LinkedList <string> scopeTokens = new LinkedList <string>();
                            /* 10) Let hint tokens be an empty set. */
                            LinkedList <string> hintTokens = new LinkedList <string>();
                            /* 11) Let IDL value have the same value as field. */
                            string idlValue = Lookup.Keyword(field);

                            /* 12) If the indexth token in tokens is the first entry, then skip to the step labeled done. */
                            //if (index == 0)
                            if (Stream.Remaining > 1)
                            {
                                /* 13) Decrement index by one. */
                                //index--;
                                Stream.Consume();
                                /* 14) If category is Contact and the indexth token in tokens is an ASCII case-insensitive match for one of the strings in the following list, then run the substeps that follow: */
                                if (category == EAutofillCategory.Contact)
                                {
                                    if (Lookup.TryEnum(Stream.Next, out EAutofillContact hint))
                                    {
                                        /* 1) Let contact be the matching string from the list above. */
                                        string contact = Stream.Next;
                                        /* 2) Insert contact at the start of scope tokens. */
                                        scopeTokens.AddFirst(contact);
                                        /* 3) Add contact to hint tokens. */
                                        hintTokens.AddLast(contact);
                                        /* 4) Let IDL value be the concatenation of contact, a U+0020 SPACE character, and the previous value of IDL value (which at this point will always be field). */
                                        idlValue = string.Concat(Stream.Next, CHAR_SPACE, idlValue);
                                        /* 5) If the indexth entry in tokens is the first entry, then skip to the step labeled done. */
                                        if (Stream.Remaining > 1)
                                        {
                                            /* 6) Decrement index by one. */
                                            //index--;
                                            Stream.Consume();
                                        }
                                    }
                                }
                                /* 15) If the indexth token in tokens is an ASCII case-insensitive match for one of the strings in the following list, then run the substeps that follow: */
                                if (Lookup.Is_Declared <EAutofillMode>(Stream.Next))
                                {
                                    /* 1) Let mode be the matching string from the list above. */
                                    var mode = Stream.Next;
                                    /* 2) Insert mode at the start of scope tokens. */
                                    scopeTokens.AddFirst(mode);
                                    /* 3) Add mode to hint tokens. */
                                    hintTokens.AddLast(mode);
                                    /* 4) Let IDL value be the concatenation of mode, a U+0020 SPACE character, and the previous value of IDL value (which at this point will either be field or the concatenation of contact, a space, and field). */
                                    idlValue = string.Concat(Stream.Next, CHAR_SPACE, idlValue);
                                    /* 5) If the indexth entry in tokens is the first entry, then skip to the step labeled done. */
                                    if (Stream.Remaining > 1)
                                    {
                                        /* 6) Decrement index by one. */
                                        //index--;
                                        Stream.Consume();
                                    }
                                }
                            }

                            /* 16) If the indexth entry in tokens is not the first entry, then jump to the step labeled default. */
                            if (Stream.Remaining == 1)
                            {
                                /* 17) If the first eight characters of the indexth token in tokens are not an ASCII case-insensitive match for the string "section-", then jump to the step labeled default. */
                                if (Stream.Next.StartsWith("section-"))
                                {
                                    /* 18) Let section be the indexth token in tokens, converted to ASCII lowercase. */
                                    string section = Stream.Next;/* Already in ascii lowercase */
                                    /* 19) Insert section at the start of scope tokens. */
                                    scopeTokens.AddFirst(section);
                                    /* 20) Let IDL value be the concatenation of section, a U+0020 SPACE character, and the previous value of IDL value. */
                                    idlValue = string.Concat(Stream.Next, CHAR_SPACE, idlValue);
                                }


                                /* 21) Done: Let the element's autofill hint set be hint tokens. */
                                outHint = hintTokens.ToArray();

                                /* 22) Let the element's autofill scope be scope tokens. */
                                outScope = scopeTokens.ToArray();

                                /* 23) Let the element's autofill field name be field. */
                                outFieldName = field;

                                /* 24) Let the element's IDL-exposed autofill value be IDL value. */
                                outidlValue = idlValue;

                                /* 25) Return. */
                                return;
                            }
                        }
                    }
                }
            }

            /* 26) Default: Let the element's IDL-exposed autofill value be the empty string, and its autofill hint set and autofill scope be empty. */
            outidlValue = string.Empty;
            outHint     = Array.Empty <string>();
            outScope    = Array.Empty <string>();

            /* 27) If the element's autocomplete attribute is wearing the autofill anchor mantle, then let the element's autofill field name be the empty string and return. */
            if (afMantle == EAutofillMantle.Anchor)
            {
                outFieldName = EAutofill.EMPTY;
                return;
            }

            /* 28) Let form be the element's form owner, if any, or null otherwise. */
            var form = element.form;

            /* 29) If form is not null and form's autocomplete attribute is in the off state, then let the element's autofill field name be "off".
             * Otherwise, let the element's autofill field name be "on". */

            outFieldName = (form != null && autocomplete != null && autocomplete.AsEnum <EAutoComplete>() == EAutoComplete.Off) ? EAutofill.Off : EAutofill.On;
        }
コード例 #23
0
ファイル: NodeIterator.cs プロジェクト: dsisco11/CssUI
        internal static void pre_removing_steps(NodeIterator nodeIterator, Node toBeRemovedNode)
        {/* Docs: https://dom.spec.whatwg.org/#nodeiterator-pre-removing-steps */
            /* 1) If toBeRemovedNode is not an inclusive ancestor of nodeIterator’s reference, or toBeRemovedNode is nodeIterator’s root, then return. */
            if (!DOMCommon.Is_Inclusive_Ancestor(toBeRemovedNode, nodeIterator.referenceNode) || ReferenceEquals(toBeRemovedNode, nodeIterator.root))
            {
                return;
            }
            /* 2) If nodeIterator’s pointer before reference is true, then: */
            if (nodeIterator.pointerBeforeReferenceNode)
            {
                /* 1) Let next be toBeRemovedNode’s first following node that is an inclusive descendant of nodeIterator’s root and is not an inclusive descendant of toBeRemovedNode, and null if there is no such node. */
                Node next = null;
                var  tree = new TreeWalker(toBeRemovedNode, ENodeFilterMask.SHOW_ALL);
                Node n    = tree.nextSibling();
                while (!ReferenceEquals(n, null))
                {
                    if (DOMCommon.Is_Inclusive_Descendant(n, nodeIterator.root) && !DOMCommon.Is_Inclusive_Descendant(n, toBeRemovedNode))
                    {
                        next = n;
                        break;
                    }
                    n = tree.nextSibling();
                }
                /* 2) If next is non-null, then set nodeIterator’s reference to next and return. */
                if (!ReferenceEquals(next, null))
                {
                    nodeIterator.referenceNode = next;
                    return;
                }
                /* 3) Otherwise, set nodeIterator’s pointer before reference to false. */
                nodeIterator.pointerBeforeReferenceNode = false;
            }
            /* 3) Set nodeIterator’s reference to toBeRemovedNode’s parent, if toBeRemovedNode’s previous sibling is null, and to the inclusive descendant of toBeRemovedNode’s previous sibling that appears last in tree order otherwise. */
            if (toBeRemovedNode.previousSibling == null)
            {
                nodeIterator.referenceNode = toBeRemovedNode.parentNode;
            }
            else
            {
                /*
                 * "the inclusive descendant of toBeRemovedNode’s previous sibling that appears last in tree order"
                 *  That would just be the previous sibling itsself yea? or is it the most-descended last child? wtf
                 */
                var  tree    = new TreeWalker(toBeRemovedNode.previousSibling, ENodeFilterMask.SHOW_ALL);
                Node n       = tree.lastChild();
                Node newNode = toBeRemovedNode.previousSibling;
                /* Find the most-descended last child */
                while (n != null)
                {
                    /* We keep going deeper until the traversal stops going deeper */
                    if (!DOMCommon.Is_Descendant(n, newNode))
                    {
                        break;
                    }

                    newNode = n;
                    n       = tree.lastChild();
                }

                nodeIterator.referenceNode = newNode;
            }
        }
コード例 #24
0
ファイル: XMLDocument.cs プロジェクト: dsisco11/CssUI
 {/* Docs: https://dom.spec.whatwg.org/#xmldocument */
     public XMLDocument(string Namespace) : base(DocumentType.XML, DOMCommon.Lookup_Content_Type_String(Namespace))
     {
     }
コード例 #25
0
ファイル: FocusableArea.cs プロジェクト: dsisco11/CssUI
        /// <summary>
        /// Returns <c>True</c> if the given <paramref name="target"/> is a valid focusable area
        /// </summary>
        public static bool Is_Focusable(EventTarget target)
        {/* Docs: https://html.spec.whatwg.org/multipage/interaction.html#focusable-area */
         /* This list may be incomplete, there may be other valid focusable areas not accounted for here */

#if ENABLE_HTML
            /* Elements that have their tabindex focus flag set, that are not actually disabled, that are not expressly inert, and that are either being rendered or being used as relevant canvas fallback content. */
            if (target is HTMLElement htmlElement)
            {
                if (htmlElement.tabindex_focus_flag && !htmlElement.is_actually_disabled && !htmlElement.isExpresslyInert)
                {
                    if (htmlElement.is_being_rendered || DOMCommon.Is_Being_Used_As_Canvas_Fallback_Content(htmlElement))
                    {
                        return(true);
                    }
                }
            }

            /* The shapes of area elements in an image map associated with an img element that is being rendered and is not expressly inert. */
            if (target is HTMLAreaElement areaElement)
            {
                /* XXX: figure this out */
            }

            if (target is Element element)
            {
                /* The contents of an iframe */
                if (element.parentElement is HTMLIFrameElement)
                {
                    return(true);
                }
            }
#endif
            /* The scrollable regions of elements that are being rendered and are not expressly inert. */
            if (target is ScrollBox scrollbox && scrollbox.Owner.is_being_rendered && !scrollbox.Owner.isExpresslyInert)
            {
                return(true);
            }

            /* The viewport of a Document that has a non-null browsing context and is not inert. */
            if (target is IViewport viewport && viewport.document.BrowsingContext != null)
            {
                return(true);
            }


            if (target is Document document)
            {
                return(true);
            }
            if (target is BrowsingContext context)
            {
                return(true);
            }



            /* The user-agent provided subwidgets of elements that are being rendered and are not actually disabled or expressly inert. */
            /* XXX: dont forget these */


            /* Any other element or part of an element, especially to aid with accessibility or to better match platform conventions. */
            /* XXX: This seems to contradict the fact that elements must have the tabindex_focus_flag set */


            return(false);
        }
コード例 #26
0
        override public bool Matches(Element E, params Node[] scopeElements)
        {
            switch (Operator)
            {
            // CSS 2.0 operators
            case ECssAttributeOperator.Isset:    // isset
            {
                return(E.hasAttribute(AttributeName));
            }

            case ECssAttributeOperator.Equals:    // equals
            {
                if (string.IsNullOrEmpty(Value))
                {
                    return(false);
                }
                return(StringCommon.StrEq(Value, E.getAttribute(AttributeName).AsString()));
            }

            case ECssAttributeOperator.PrefixedWith:    // equals or prefixed-with
            {
                if (!E.hasAttribute(AttributeName))
                {
                    return(false);
                }
                string val = E.getAttribute(AttributeName).AsString();
                if (StringCommon.StrEq(Value, val))
                {
                    return(true);
                }
                if (val.StartsWith(string.Concat(Value, '-')))
                {
                    return(true);
                }
                return(false);
            }

            case ECssAttributeOperator.Includes:    // list-contains
            {
                if (string.IsNullOrEmpty(Value))
                {
                    return(false);
                }
                /* First lets check the elements token-list map */
                if (E.tokenListMap.TryGetValue(AttributeName, out IAttributeTokenList listMap))
                {
                    var TokenList = (AttributeTokenList <string>)listMap;
                    return(TokenList.Contains(Value));
                }

                if (!E.hasAttribute(AttributeName))
                {
                    return(false);
                }
                var attr = E.getAttribute(AttributeName);
                var set  = DOMCommon.Parse_Ordered_Set(attr.AsAtomic().AsMemory());
                return(set.Contains(Value.AsMemory()));
            }

            // Sub-string operators
            case ECssAttributeOperator.StartsWith:    // starts-with
            {
                if (string.IsNullOrEmpty(Value))
                {
                    return(false);
                }
                if (!E.hasAttribute(AttributeName))
                {
                    return(false);
                }
                var attr = E.getAttribute(AttributeName);
                return(attr.AsString().StartsWith(Value));
            }

            case ECssAttributeOperator.EndsWith:    // ends-with
            {
                if (string.IsNullOrEmpty(Value))
                {
                    return(false);
                }
                if (!E.hasAttribute(AttributeName))
                {
                    return(false);
                }
                var attr = E.getAttribute(AttributeName);
                return(attr.AsString().EndsWith(Value));
            }

            case ECssAttributeOperator.Contains:    // contains
            {
                if (string.IsNullOrEmpty(Value))
                {
                    return(false);
                }
                if (!E.hasAttribute(AttributeName))
                {
                    return(false);
                }
                var attr = E.getAttribute(AttributeName);
                return(StringCommon.Contains(attr.AsAtomic().AsMemory().Span, Value.AsMemory().Span));
            }

            default:
                throw new CssSelectorException($"Attribute selector operator ({Enum.GetName(typeof(ECssAttributeOperator), Operator)}) logic not implemented!");
            }
        }