internal static async Task <IEnumerable <Common.IDocument> > ProcessElementsAsync(
            Common.IDocument input,
            IExecutionContext context,
            string querySelector,
            bool first,
            Action <Common.IDocument, IExecutionContext, IElement, Dictionary <string, object> > processElement)
        {
            // Parse the HTML content
            IHtmlDocument htmlDocument = await input.ParseHtmlAsync(context, HtmlParser);

            if (htmlDocument == null)
            {
                return(input.Yield());
            }

            // Evaluate the query selector
            try
            {
                if (!string.IsNullOrWhiteSpace(querySelector))
                {
                    IElement[] elements = first
                        ? new[] { htmlDocument.QuerySelector(querySelector) }
                        : htmlDocument.QuerySelectorAll(querySelector).ToArray();
                    if (elements.Length > 0 && elements[0] != null)
                    {
                        INode clone = htmlDocument.Clone(true);  // Clone the document so we know if it changed
                        Dictionary <string, object> metadata = new Dictionary <string, object>();
                        foreach (IElement element in elements)
                        {
                            processElement(input, context, element, metadata);
                        }

                        if (htmlDocument.Equals(clone))
                        {
                            // Elements were not edited so return the original document or clone it with new metadata
                            return(metadata.Count == 0 ? input.Yield() : input.Clone(metadata).Yield());
                        }

                        // Elements were edited so get the new content
                        using (Stream contentStream = await context.GetContentStreamAsync())
                        {
                            using (StreamWriter writer = contentStream.GetWriter())
                            {
                                htmlDocument.ToHtml(writer, ProcessingInstructionFormatter.Instance);
                                writer.Flush();
                                IContentProvider contentProvider = context.GetContentProvider(contentStream, MediaTypes.Html);
                                return(metadata.Count == 0
                                    ? input.Clone(contentProvider).Yield()
                                    : input.Clone(metadata, contentProvider).Yield());
                            }
                        }
                    }
                }
                return(input.Yield());
            }
            catch (Exception ex)
            {
                context.LogWarning("Exception while processing HTML for {0}: {1}", input.ToSafeDisplayString(), ex.Message);
                return(input.Yield());
            }
        }