/// <summary>
        /// WinLive 107762: In IE8, the cursor can get stuck if a user continues to tap an arrow key in a single
        /// direction. To work around the issue, we wrap the &lt;br&gt;s in a single &lt;div&gt;.
        /// </summary>
        protected void FixUpStickyBrs(MarkupRange range)
        {
            IHTMLElementFilter brFilter = ElementFilters.CreateTagIdFilter(MarkupServices.GetNameForTagId(_ELEMENT_TAG_ID.TAGID_BR));

            foreach (IHTMLElement br in range.GetElements(brFilter, true))
            {
                MarkupRange brRange = MarkupServices.CreateMarkupRange(br, true);

                // We want to be moving from left to right to get the correct _MARKUP_CONTEXT_TYPE.
                brRange.Start.Left(true);
                MarkupContext beforeBr = brRange.Start.Right(true);
                MarkupContext afterBr = brRange.End.Right(false);

                if (beforeBr.Element is IHTMLDivElement && afterBr.Element is IHTMLDivElement)
                {
                    // There are three cases that IE8 screws up:
                    //   1. <div><br><div>...</div></div>
                    //   2. <div>...</div><br><div>...</div>
                    //   3. <div><div>...</div><br></div>
                    // The solution is to make sure the <br> is directly wrapped in a single <div>:
                    //   ...<div><br></div>...
                    if (!(beforeBr.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope &&
                        afterBr.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope))
                    {
                        HtmlStyleHelper.WrapRangeInElement(MarkupServices, brRange, _ELEMENT_TAG_ID.TAGID_DIV);
                    }
                }
            }
        }
        /// <summary>
        /// WinLive 209110: When IE renders tables in quirks mode, the table inherits its font color from the body
        /// element. We want the table to inherit its font color normally, so we force it to.
        /// </summary>
        protected void ForceTablesToInheritFontColor(MarkupRange range)
        {
            IHTMLElementFilter tableFilter = ElementFilters.CreateTagIdFilter(MarkupServices.GetNameForTagId(_ELEMENT_TAG_ID.TAGID_TABLE));

            foreach (IHTMLElement table in range.GetElements(tableFilter, true))
            {
                if (table.style.color == null)
                {
                    IHTMLElement2 tableParent = (IHTMLElement2)table.parentElement;
                    if (tableParent != null)
                    {
                        // Setting the color to "inherit" doesn't work. We have to manually specify a color.
                        table.style.color = tableParent.currentStyle.color;
                    }
                }
            }
        }
        /// <summary>
        /// Makes sure that whole (not parts of) lists are included in the source of a paste.
        /// </summary>
        /// <param name="range">The original source range. The range may be modified.</param>
        /// <param name="markupServices">MarkupServices for the range.</param>
        private void ExpandToIncludeLists(MarkupRange range, MshtmlMarkupServices markupServices)
        {
            MarkupPointer pointer = markupServices.CreateMarkupPointer();
            IHTMLElementFilter listFilter =
                ElementFilters.CreateCompoundElementFilter(ElementFilters.LIST_ELEMENTS, ElementFilters.LIST_ITEM_ELEMENTS);

            IHTMLElement[] listElements = range.GetElements(listFilter, false);
            foreach (IHTMLElement element in listElements)
            {
                IHTMLElement parentList = element;
                while (parentList != null && !ElementFilters.IsListElement(parentList))
                {
                    parentList = parentList.parentElement;
                }

                if (parentList != null)
                {
                    pointer.MoveAdjacentToElement(parentList, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin);
                    if (range.Start.IsRightOf(pointer))
                    {
                        range.Start.MoveToPointer(pointer);
                    }

                    pointer.MoveAdjacentToElement(parentList, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd);
                    if (range.End.IsLeftOf(pointer))
                    {
                        range.End.MoveToPointer(pointer);
                    }
                }
            }
        }
        protected void InflateEmptyParagraphs(MarkupRange range)
        {
            IHTMLElementFilter paragraphFilter = ElementFilters.CreateCompoundElementFilter(
                    ElementFilters.CreateTagIdFilter("p"),
                    ElementFilters.CreateTagIdFilter("div")
                );

            foreach (IHTMLElement paragraphElement in range.GetElements(paragraphFilter, true))
            {
                // We want only the "empty" paragraphs (e.g. <p>&nbsp;</p> or <p><br /></p>).
                bool paragraphIsEmpty = false;

                string innerHtml = paragraphElement.innerHTML ?? string.Empty;
                if (innerHtml.Equals("&nbsp;", StringComparison.OrdinalIgnoreCase))
                {
                    // Covers the case of <p>&nbsp;</p>.
                    paragraphIsEmpty = true;
                }
                else if (String.IsNullOrEmpty(paragraphElement.innerText))
                {
                    IHTMLElementCollection children = (IHTMLElementCollection)paragraphElement.children;
                    if (children != null && children.length == 1)
                    {
                        IHTMLElementCollection childBrs = (IHTMLElementCollection)children.tags("br");
                        if (childBrs != null && childBrs.length == 1)
                        {
                            // Covers the case of <p><br></p>.
                            paragraphIsEmpty = true;
                        }
                    }
                }

                if (paragraphIsEmpty)
                {
                    // Force MSHTML to render it even though its empty.
                    IHTMLElement3 paragraphElement3 = (IHTMLElement3)paragraphElement;
                    paragraphElement3.inflateBlock = true;

                    // MSHTML will add the &nbsp; back in anyway, but in a special way so that it does not appear
                    // selectable to the user. It seems to be their internal way of doing inflateBlock.
                    paragraphElement.innerHTML = "";
                }
            }
        }
        /// <summary>
        /// Makes sure that whole (not parts of) tables are included in the source of a paste.
        /// </summary>
        /// <param name="range">The original source range. The range may be modified.</param>
        /// <param name="markupServices">MarkupServices for the range.</param>
        private void ExpandToIncludeTables(MarkupRange range, MshtmlMarkupServices markupServices)
        {
            MarkupPointer pointer = markupServices.CreateMarkupPointer();

            IHTMLElement[] tableElements = range.GetElements(ElementFilters.TABLE_ELEMENTS, false);
            foreach (IHTMLElement element in tableElements)
            {
                IHTMLElement parentTable = element;
                while (parentTable != null && markupServices.GetElementTagId(parentTable) != _ELEMENT_TAG_ID.TAGID_TABLE)
                {
                    parentTable = parentTable.parentElement;
                }

                if (parentTable != null)
                {
                    pointer.MoveAdjacentToElement(parentTable, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin);
                    if (range.Start.IsRightOf(pointer))
                    {
                        range.Start.MoveToPointer(pointer);
                    }

                    pointer.MoveAdjacentToElement(parentTable, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd);
                    if (range.End.IsLeftOf(pointer))
                    {
                        range.End.MoveToPointer(pointer);
                    }
                }
            }
        }
        public static void RemoveAttributes(MshtmlMarkupServices markupServices, MarkupRange selection, string[] attributesToRemove)
        {
            IHTMLElementFilter[] filters = new IHTMLElementFilter[attributesToRemove.Length];

            for (int i = 0; i < attributesToRemove.Length; i++)
                filters[i] = ElementFilters.CreateElementAttributeFilter(attributesToRemove[i]);

            IHTMLElement[] elements = selection.GetElements(ElementFilters.CreateCompoundElementFilter(filters), false);
            foreach (IHTMLElement element in elements)
            {
                foreach (string attribute in attributesToRemove)
                    element.removeAttribute(attribute, 0);
            }
        }
        public static void ClearBackgroundColor(MshtmlMarkupServices markupServices, MarkupRange selection)
        {
            HtmlStyleHelper htmlStyleHelper = new HtmlStyleHelper(markupServices);

            htmlStyleHelper.SplitInlineTags(selection.Start);
            htmlStyleHelper.SplitInlineTags(selection.End);

            IHTMLElement[] elements = selection.GetElements(ElementFilters.CreateTagIdFilter("font"), false);
            foreach (IHTMLElement element in elements)
            {
                element.style.backgroundColor = "";
            }

            // We may now be left with empty font tags, e.g. <font>blah</font>.
            // After switching between editors this becomes <font size="+0">blah</font>, which
            // causes blah to be rendered differently.
            // To avoid that we need to remove any empty-attribute font tags.
            selection.RemoveElementsByTagId(_ELEMENT_TAG_ID.TAGID_FONT, true);
        }
 public static string[] GetSmartContentIds(MarkupRange range)
 {
     ArrayList ids = new ArrayList();
     foreach (IHTMLElement el in range.GetElements(new IHTMLElementFilter(IsSmartContentContainer), false))
     {
         if (el.id != null)
             ids.Add(el.id);
     }
     return (string[])ids.ToArray(typeof(string));
 }
        private void FindCellRange(MarkupRange selectedRange, out ArrayList selectedCells, out IHTMLTableCell beginCell, out IHTMLTableCell endCell)
        {
            // default to null
            beginCell = null;
            endCell = null;
            selectedCells = new ArrayList();

            // JJA: fix bug #476623 -- at document initialization the selected markup range
            // may not yet be positioned so protect ourselves in this case
            if (!selectedRange.Positioned)
                return;

            // query for all of the table cells within the range
            selectedCells.AddRange(selectedRange.GetElements(ElementFilters.TABLE_CELL_ELEMENT, false));

            // extract the begin and end cells
            if (selectedCells.Count == 0)
            {
                // see if the selection is contained within a single cell
                beginCell = selectedRange.Start.GetParentElement(ElementFilters.TABLE_CELL_ELEMENT) as IHTMLTableCell;
                if (beginCell != null)
                {
                    // make sure the cell is content editable (it would not be in the case
                    // where the call to GetParentElement went all the way out of the body
                    // and found a cell that was part of the containing template)
                    if (!(beginCell as IHTMLElement3).isContentEditable)
                        beginCell = null;
                }

                endCell = beginCell;

                selectedCells.Add(beginCell);
            }
            else if (selectedCells.Count == 1)
            {
                beginCell = selectedCells[0] as IHTMLTableCell;
                endCell = selectedCells[0] as IHTMLTableCell;
            }
            else
            {
                beginCell = selectedCells[0] as IHTMLTableCell;
                endCell = selectedCells[selectedCells.Count - 1] as IHTMLTableCell;
            }
        }