diff -r 000000000000 -r 4f2f89ce4247 WebCore/html/HTMLConstructionSite.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/WebCore/html/HTMLConstructionSite.cpp Fri Sep 17 09:02:29 2010 +0300
@@ -0,0 +1,450 @@
+/*
+ * Copyright (C) 2010 Google, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "HTMLTreeBuilder.h"
+
+#include "Comment.h"
+#include "DocumentFragment.h"
+#include "DocumentType.h"
+#include "Element.h"
+#include "Frame.h"
+#include "HTMLDocument.h"
+#include "HTMLElementFactory.h"
+#include "HTMLFormElement.h"
+#include "HTMLHtmlElement.h"
+#include "HTMLNames.h"
+#include "HTMLScriptElement.h"
+#include "HTMLToken.h"
+#include "HTMLTokenizer.h"
+#include "LegacyHTMLDocumentParser.h"
+#include "LegacyHTMLTreeBuilder.h"
+#include "LocalizedStrings.h"
+#if ENABLE(MATHML)
+#include "MathMLNames.h"
+#endif
+#include "NotImplemented.h"
+#if ENABLE(SVG)
+#include "SVGNames.h"
+#endif
+#include "ScriptController.h"
+#include "Settings.h"
+#include "Text.h"
+#include
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+namespace {
+
+bool hasImpliedEndTag(Element* element)
+{
+ return element->hasTagName(ddTag)
+ || element->hasTagName(dtTag)
+ || element->hasTagName(liTag)
+ || element->hasTagName(optionTag)
+ || element->hasTagName(optgroupTag)
+ || element->hasTagName(pTag)
+ || element->hasTagName(rpTag)
+ || element->hasTagName(rtTag);
+}
+
+bool causesFosterParenting(const QualifiedName& tagName)
+{
+ return tagName == tableTag
+ || tagName == tbodyTag
+ || tagName == tfootTag
+ || tagName == theadTag
+ || tagName == trTag;
+}
+
+} // namespace
+
+template
+PassRefPtr HTMLConstructionSite::attach(Node* parent, PassRefPtr prpChild)
+{
+ RefPtr child = prpChild;
+
+ // FIXME: It's confusing that HTMLConstructionSite::attach does the magic
+ // redirection to the foster parent but HTMLConstructionSite::attachAtSite
+ // doesn't. It feels like we're missing a concept somehow.
+ if (shouldFosterParent()) {
+ fosterParent(child.get());
+ ASSERT(child->attached());
+ return child.release();
+ }
+
+ parent->parserAddChild(child);
+ // It's slightly unfortunate that we need to hold a reference to child
+ // here to call attach(). We should investigate whether we can rely on
+ // |parent| to hold a ref at this point. In the common case (at least
+ // for elements), however, we'll get to use this ref in the stack of
+ // open elements.
+ if (parent->attached()) {
+ ASSERT(!child->attached());
+ child->attach();
+ }
+ return child.release();
+}
+
+void HTMLConstructionSite::attachAtSite(const AttachmentSite& site, PassRefPtr prpChild)
+{
+ RefPtr child = prpChild;
+
+ if (site.nextChild) {
+ // FIXME: We need an insertElement which does not send mutation events.
+ ExceptionCode ec = 0;
+ site.parent->insertBefore(child, site.nextChild, ec);
+ ASSERT(!ec);
+ if (site.parent->attached() && !child->attached())
+ child->attach();
+ return;
+ }
+ site.parent->parserAddChild(child);
+ // It's slightly unfortunate that we need to hold a reference to child
+ // here to call attach(). We should investigate whether we can rely on
+ // |site.parent| to hold a ref at this point.
+ if (site.parent->attached() && !child->attached())
+ child->attach();
+}
+
+HTMLConstructionSite::HTMLConstructionSite(Document* document, FragmentScriptingPermission scriptingPermission, bool isParsingFragment)
+ : m_document(document)
+ , m_fragmentScriptingPermission(scriptingPermission)
+ , m_isParsingFragment(isParsingFragment)
+ , m_redirectAttachToFosterParent(false)
+{
+}
+
+HTMLConstructionSite::~HTMLConstructionSite()
+{
+}
+
+PassRefPtr HTMLConstructionSite::takeForm()
+{
+ return m_form.release();
+}
+
+void HTMLConstructionSite::dispatchDocumentElementAvailableIfNeeded()
+{
+ if (m_document->frame() && !m_isParsingFragment)
+ m_document->frame()->loader()->dispatchDocumentElementAvailable();
+}
+
+void HTMLConstructionSite::insertHTMLHtmlStartTagBeforeHTML(AtomicHTMLToken& token)
+{
+ RefPtr element = HTMLHtmlElement::create(m_document);
+ element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission);
+ m_openElements.pushHTMLHtmlElement(attach(m_document, element.release()));
+ dispatchDocumentElementAvailableIfNeeded();
+}
+
+void HTMLConstructionSite::mergeAttributesFromTokenIntoElement(AtomicHTMLToken& token, Element* element)
+{
+ if (!token.attributes())
+ return;
+
+ NamedNodeMap* attributes = element->attributes(false);
+ for (unsigned i = 0; i < token.attributes()->length(); ++i) {
+ Attribute* attribute = token.attributes()->attributeItem(i);
+ if (!attributes->getAttributeItem(attribute->name()))
+ element->setAttribute(attribute->name(), attribute->value());
+ }
+}
+
+void HTMLConstructionSite::insertHTMLHtmlStartTagInBody(AtomicHTMLToken& token)
+{
+ // FIXME: parse error
+ mergeAttributesFromTokenIntoElement(token, m_openElements.htmlElement());
+}
+
+void HTMLConstructionSite::insertHTMLBodyStartTagInBody(AtomicHTMLToken& token)
+{
+ // FIXME: parse error
+ notImplemented(); // fragment case
+ mergeAttributesFromTokenIntoElement(token, m_openElements.bodyElement());
+}
+
+void HTMLConstructionSite::insertDoctype(AtomicHTMLToken& token)
+{
+ ASSERT(token.type() == HTMLToken::DOCTYPE);
+ attach(m_document, DocumentType::create(m_document, token.name(), String::adopt(token.publicIdentifier()), String::adopt(token.systemIdentifier())));
+ // FIXME: Move quirks mode detection from DocumentType element to here.
+ notImplemented();
+ if (token.forceQuirks())
+ m_document->setParseMode(Document::Compat);
+}
+
+void HTMLConstructionSite::insertComment(AtomicHTMLToken& token)
+{
+ ASSERT(token.type() == HTMLToken::Comment);
+ attach(currentElement(), Comment::create(m_document, token.comment()));
+}
+
+void HTMLConstructionSite::insertCommentOnDocument(AtomicHTMLToken& token)
+{
+ ASSERT(token.type() == HTMLToken::Comment);
+ attach(m_document, Comment::create(m_document, token.comment()));
+}
+
+void HTMLConstructionSite::insertCommentOnHTMLHtmlElement(AtomicHTMLToken& token)
+{
+ ASSERT(token.type() == HTMLToken::Comment);
+ attach(m_openElements.htmlElement(), Comment::create(m_document, token.comment()));
+}
+
+PassRefPtr HTMLConstructionSite::attachToCurrent(PassRefPtr child)
+{
+ return attach(currentElement(), child);
+}
+
+void HTMLConstructionSite::insertHTMLHtmlElement(AtomicHTMLToken& token)
+{
+ ASSERT(!shouldFosterParent());
+ m_openElements.pushHTMLHtmlElement(attachToCurrent(createHTMLElement(token)));
+ dispatchDocumentElementAvailableIfNeeded();
+}
+
+void HTMLConstructionSite::insertHTMLHeadElement(AtomicHTMLToken& token)
+{
+ ASSERT(!shouldFosterParent());
+ m_head = attachToCurrent(createHTMLElement(token));
+ m_openElements.pushHTMLHeadElement(m_head);
+}
+
+void HTMLConstructionSite::insertHTMLBodyElement(AtomicHTMLToken& token)
+{
+ ASSERT(!shouldFosterParent());
+ m_openElements.pushHTMLBodyElement(attachToCurrent(createHTMLElement(token)));
+}
+
+void HTMLConstructionSite::insertHTMLFormElement(AtomicHTMLToken& token)
+{
+ insertHTMLElement(token);
+ ASSERT(currentElement()->isHTMLElement());
+ ASSERT(currentElement()->hasTagName(formTag));
+ m_form = static_cast(currentElement());
+}
+
+void HTMLConstructionSite::insertHTMLElement(AtomicHTMLToken& token)
+{
+ m_openElements.push(attachToCurrent(createHTMLElement(token)));
+}
+
+void HTMLConstructionSite::insertSelfClosingHTMLElement(AtomicHTMLToken& token)
+{
+ ASSERT(token.type() == HTMLToken::StartTag);
+ RefPtr element = attachToCurrent(createHTMLElement(token));
+ // Normally HTMLElementStack is responsible for calling finishParsingChildren,
+ // but self-closing elements are never in the element stack so the stack
+ // doesn't get a chance to tell them that we're done parsing their children.
+ element->finishParsingChildren();
+ // FIXME: Do we want to acknowledge the token's self-closing flag?
+ // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#acknowledge-self-closing-flag
+}
+
+void HTMLConstructionSite::insertFormattingElement(AtomicHTMLToken& token)
+{
+ // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#the-stack-of-open-elements
+ // Possible active formatting elements include:
+ // a, b, big, code, em, font, i, nobr, s, small, strike, strong, tt, and u.
+ insertHTMLElement(token);
+ m_activeFormattingElements.append(currentElement());
+}
+
+void HTMLConstructionSite::insertScriptElement(AtomicHTMLToken& token)
+{
+ RefPtr element = HTMLScriptElement::create(scriptTag, m_document, true);
+ element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission);
+ m_openElements.push(attachToCurrent(element.release()));
+}
+
+void HTMLConstructionSite::insertForeignElement(AtomicHTMLToken& token, const AtomicString& namespaceURI)
+{
+ ASSERT(token.type() == HTMLToken::StartTag);
+ notImplemented(); // parseError when xmlns or xmlns:xlink are wrong.
+
+ RefPtr element = attachToCurrent(createElement(token, namespaceURI));
+ if (!token.selfClosing())
+ m_openElements.push(element);
+}
+
+void HTMLConstructionSite::insertTextNode(const String& characters)
+{
+ AttachmentSite site;
+ site.parent = currentElement();
+ site.nextChild = 0;
+ if (shouldFosterParent())
+ findFosterSite(site);
+
+ Node* previousChild = site.nextChild ? site.nextChild->previousSibling() : site.parent->lastChild();
+ if (previousChild && previousChild->isTextNode()) {
+ // FIXME: We're only supposed to append to this text node if it
+ // was the last text node inserted by the parser.
+ CharacterData* textNode = static_cast(previousChild);
+ textNode->parserAppendData(characters);
+ return;
+ }
+
+ attachAtSite(site, Text::create(m_document, characters));
+}
+
+PassRefPtr HTMLConstructionSite::createElement(AtomicHTMLToken& token, const AtomicString& namespaceURI)
+{
+ QualifiedName tagName(nullAtom, token.name(), namespaceURI);
+ RefPtr element = m_document->createElement(tagName, true);
+ element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission);
+ return element.release();
+}
+
+PassRefPtr HTMLConstructionSite::createHTMLElement(AtomicHTMLToken& token)
+{
+ QualifiedName tagName(nullAtom, token.name(), xhtmlNamespaceURI);
+ // FIXME: This can't use HTMLConstructionSite::createElement because we
+ // have to pass the current form element. We should rework form association
+ // to occur after construction to allow better code sharing here.
+ RefPtr element = HTMLElementFactory::createHTMLElement(tagName, m_document, form(), true);
+ element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission);
+ ASSERT(element->isHTMLElement());
+ return element.release();
+}
+
+PassRefPtr HTMLConstructionSite::createHTMLElementFromElementRecord(HTMLElementStack::ElementRecord* record)
+{
+ return createHTMLElementFromSavedElement(record->element());
+}
+
+namespace {
+
+PassRefPtr cloneAttributes(Element* element)
+{
+ NamedNodeMap* attributes = element->attributes(true);
+ if (!attributes)
+ return 0;
+
+ RefPtr newAttributes = NamedNodeMap::create();
+ for (size_t i = 0; i < attributes->length(); ++i) {
+ Attribute* attribute = attributes->attributeItem(i);
+ RefPtr clone = Attribute::createMapped(attribute->name(), attribute->value());
+ newAttributes->addAttribute(clone);
+ }
+ return newAttributes.release();
+}
+
+}
+
+PassRefPtr HTMLConstructionSite::createHTMLElementFromSavedElement(Element* element)
+{
+ // FIXME: This method is wrong. We should be using the original token.
+ // Using an Element* causes us to fail examples like this:
+ // TEXT
+ // When reconstructTheActiveFormattingElements calls this method to open
+ // a second tag to wrap TEXT, it will have id "2", even though the HTML5
+ // spec implies it should be "1". Minefield matches the HTML5 spec here.
+
+ ASSERT(element->isHTMLElement()); // otherwise localName() might be wrong.
+ AtomicHTMLToken fakeToken(HTMLToken::StartTag, element->localName(), cloneAttributes(element));
+ return createHTMLElement(fakeToken);
+}
+
+bool HTMLConstructionSite::indexOfFirstUnopenFormattingElement(unsigned& firstUnopenElementIndex) const
+{
+ if (m_activeFormattingElements.isEmpty())
+ return false;
+ unsigned index = m_activeFormattingElements.size();
+ do {
+ --index;
+ const HTMLFormattingElementList::Entry& entry = m_activeFormattingElements.at(index);
+ if (entry.isMarker() || m_openElements.contains(entry.element())) {
+ firstUnopenElementIndex = index + 1;
+ return firstUnopenElementIndex < m_activeFormattingElements.size();
+ }
+ } while (index);
+ firstUnopenElementIndex = index;
+ return true;
+}
+
+void HTMLConstructionSite::reconstructTheActiveFormattingElements()
+{
+ unsigned firstUnopenElementIndex;
+ if (!indexOfFirstUnopenFormattingElement(firstUnopenElementIndex))
+ return;
+
+ unsigned unopenEntryIndex = firstUnopenElementIndex;
+ ASSERT(unopenEntryIndex < m_activeFormattingElements.size());
+ for (; unopenEntryIndex < m_activeFormattingElements.size(); ++unopenEntryIndex) {
+ HTMLFormattingElementList::Entry& unopenedEntry = m_activeFormattingElements.at(unopenEntryIndex);
+ RefPtr reconstructed = createHTMLElementFromSavedElement(unopenedEntry.element());
+ m_openElements.push(attachToCurrent(reconstructed.release()));
+ unopenedEntry.replaceElement(currentElement());
+ }
+}
+
+void HTMLConstructionSite::generateImpliedEndTagsWithExclusion(const AtomicString& tagName)
+{
+ while (hasImpliedEndTag(currentElement()) && !currentElement()->hasLocalName(tagName))
+ m_openElements.pop();
+}
+
+void HTMLConstructionSite::generateImpliedEndTags()
+{
+ while (hasImpliedEndTag(currentElement()))
+ m_openElements.pop();
+}
+
+void HTMLConstructionSite::findFosterSite(AttachmentSite& site)
+{
+ HTMLElementStack::ElementRecord* lastTableElementRecord = m_openElements.topmost(tableTag.localName());
+ if (lastTableElementRecord) {
+ Element* lastTableElement = lastTableElementRecord->element();
+ if (Node* parent = lastTableElement->parent()) {
+ site.parent = parent;
+ site.nextChild = lastTableElement;
+ return;
+ }
+ site.parent = lastTableElementRecord->next()->element();
+ site.nextChild = 0;
+ return;
+ }
+ // Fragment case
+ site.parent = m_openElements.bottom(); // element
+ site.nextChild = 0;
+}
+
+bool HTMLConstructionSite::shouldFosterParent() const
+{
+ return m_redirectAttachToFosterParent
+ && causesFosterParenting(currentElement()->tagQName());
+}
+
+void HTMLConstructionSite::fosterParent(Node* node)
+{
+ AttachmentSite site;
+ findFosterSite(site);
+ attachAtSite(site, node);
+}
+
+}