WebCore/html/HTMLConstructionSite.cpp
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     1 /*
       
     2  * Copyright (C) 2010 Google, Inc. All Rights Reserved.
       
     3  *
       
     4  * Redistribution and use in source and binary forms, with or without
       
     5  * modification, are permitted provided that the following conditions
       
     6  * are met:
       
     7  * 1. Redistributions of source code must retain the above copyright
       
     8  *    notice, this list of conditions and the following disclaimer.
       
     9  * 2. Redistributions in binary form must reproduce the above copyright
       
    10  *    notice, this list of conditions and the following disclaimer in the
       
    11  *    documentation and/or other materials provided with the distribution.
       
    12  *
       
    13  * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
       
    14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
       
    15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
       
    16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL GOOGLE INC. OR
       
    17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
       
    18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
       
    19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
       
    20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
       
    21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
       
    22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
       
    23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       
    24  */
       
    25 
       
    26 #include "config.h"
       
    27 #include "HTMLTreeBuilder.h"
       
    28 
       
    29 #include "Comment.h"
       
    30 #include "DocumentFragment.h"
       
    31 #include "DocumentType.h"
       
    32 #include "Element.h"
       
    33 #include "Frame.h"
       
    34 #include "HTMLDocument.h"
       
    35 #include "HTMLElementFactory.h"
       
    36 #include "HTMLFormElement.h"
       
    37 #include "HTMLHtmlElement.h"
       
    38 #include "HTMLNames.h"
       
    39 #include "HTMLScriptElement.h"
       
    40 #include "HTMLToken.h"
       
    41 #include "HTMLTokenizer.h"
       
    42 #include "LegacyHTMLDocumentParser.h"
       
    43 #include "LegacyHTMLTreeBuilder.h"
       
    44 #include "LocalizedStrings.h"
       
    45 #if ENABLE(MATHML)
       
    46 #include "MathMLNames.h"
       
    47 #endif
       
    48 #include "NotImplemented.h"
       
    49 #if ENABLE(SVG)
       
    50 #include "SVGNames.h"
       
    51 #endif
       
    52 #include "ScriptController.h"
       
    53 #include "Settings.h"
       
    54 #include "Text.h"
       
    55 #include <wtf/UnusedParam.h>
       
    56 
       
    57 namespace WebCore {
       
    58 
       
    59 using namespace HTMLNames;
       
    60 
       
    61 namespace {
       
    62 
       
    63 bool hasImpliedEndTag(Element* element)
       
    64 {
       
    65     return element->hasTagName(ddTag)
       
    66         || element->hasTagName(dtTag)
       
    67         || element->hasTagName(liTag)
       
    68         || element->hasTagName(optionTag)
       
    69         || element->hasTagName(optgroupTag)
       
    70         || element->hasTagName(pTag)
       
    71         || element->hasTagName(rpTag)
       
    72         || element->hasTagName(rtTag);
       
    73 }
       
    74 
       
    75 bool causesFosterParenting(const QualifiedName& tagName)
       
    76 {
       
    77     return tagName == tableTag
       
    78         || tagName == tbodyTag
       
    79         || tagName == tfootTag
       
    80         || tagName == theadTag
       
    81         || tagName == trTag;
       
    82 }
       
    83 
       
    84 } // namespace
       
    85 
       
    86 template<typename ChildType>
       
    87 PassRefPtr<ChildType> HTMLConstructionSite::attach(Node* parent, PassRefPtr<ChildType> prpChild)
       
    88 {
       
    89     RefPtr<ChildType> child = prpChild;
       
    90 
       
    91     // FIXME: It's confusing that HTMLConstructionSite::attach does the magic
       
    92     // redirection to the foster parent but HTMLConstructionSite::attachAtSite
       
    93     // doesn't.  It feels like we're missing a concept somehow.
       
    94     if (shouldFosterParent()) {
       
    95         fosterParent(child.get());
       
    96         ASSERT(child->attached());
       
    97         return child.release();
       
    98     }
       
    99 
       
   100     parent->parserAddChild(child);
       
   101     // It's slightly unfortunate that we need to hold a reference to child
       
   102     // here to call attach().  We should investigate whether we can rely on
       
   103     // |parent| to hold a ref at this point.  In the common case (at least
       
   104     // for elements), however, we'll get to use this ref in the stack of
       
   105     // open elements.
       
   106     if (parent->attached()) {
       
   107         ASSERT(!child->attached());
       
   108         child->attach();
       
   109     }
       
   110     return child.release();
       
   111 }
       
   112 
       
   113 void HTMLConstructionSite::attachAtSite(const AttachmentSite& site, PassRefPtr<Node> prpChild)
       
   114 {
       
   115     RefPtr<Node> child = prpChild;
       
   116 
       
   117     if (site.nextChild) {
       
   118         // FIXME: We need an insertElement which does not send mutation events.
       
   119         ExceptionCode ec = 0;
       
   120         site.parent->insertBefore(child, site.nextChild, ec);
       
   121         ASSERT(!ec);
       
   122         if (site.parent->attached() && !child->attached())
       
   123             child->attach();
       
   124         return;
       
   125     }
       
   126     site.parent->parserAddChild(child);
       
   127     // It's slightly unfortunate that we need to hold a reference to child
       
   128     // here to call attach().  We should investigate whether we can rely on
       
   129     // |site.parent| to hold a ref at this point.
       
   130     if (site.parent->attached() && !child->attached())
       
   131         child->attach();
       
   132 }
       
   133 
       
   134 HTMLConstructionSite::HTMLConstructionSite(Document* document, FragmentScriptingPermission scriptingPermission, bool isParsingFragment)
       
   135     : m_document(document)
       
   136     , m_fragmentScriptingPermission(scriptingPermission)
       
   137     , m_isParsingFragment(isParsingFragment)
       
   138     , m_redirectAttachToFosterParent(false)
       
   139 {
       
   140 }
       
   141 
       
   142 HTMLConstructionSite::~HTMLConstructionSite()
       
   143 {
       
   144 }
       
   145 
       
   146 PassRefPtr<HTMLFormElement> HTMLConstructionSite::takeForm()
       
   147 {
       
   148     return m_form.release();
       
   149 }
       
   150 
       
   151 void HTMLConstructionSite::dispatchDocumentElementAvailableIfNeeded()
       
   152 {
       
   153     if (m_document->frame() && !m_isParsingFragment)
       
   154         m_document->frame()->loader()->dispatchDocumentElementAvailable();
       
   155 }
       
   156 
       
   157 void HTMLConstructionSite::insertHTMLHtmlStartTagBeforeHTML(AtomicHTMLToken& token)
       
   158 {
       
   159     RefPtr<Element> element = HTMLHtmlElement::create(m_document);
       
   160     element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission);
       
   161     m_openElements.pushHTMLHtmlElement(attach(m_document, element.release()));
       
   162     dispatchDocumentElementAvailableIfNeeded();
       
   163 }
       
   164 
       
   165 void HTMLConstructionSite::mergeAttributesFromTokenIntoElement(AtomicHTMLToken& token, Element* element)
       
   166 {
       
   167     if (!token.attributes())
       
   168         return;
       
   169 
       
   170     NamedNodeMap* attributes = element->attributes(false);
       
   171     for (unsigned i = 0; i < token.attributes()->length(); ++i) {
       
   172         Attribute* attribute = token.attributes()->attributeItem(i);
       
   173         if (!attributes->getAttributeItem(attribute->name()))
       
   174             element->setAttribute(attribute->name(), attribute->value());
       
   175     }
       
   176 }
       
   177 
       
   178 void HTMLConstructionSite::insertHTMLHtmlStartTagInBody(AtomicHTMLToken& token)
       
   179 {
       
   180     // FIXME: parse error
       
   181     mergeAttributesFromTokenIntoElement(token, m_openElements.htmlElement());
       
   182 }
       
   183 
       
   184 void HTMLConstructionSite::insertHTMLBodyStartTagInBody(AtomicHTMLToken& token)
       
   185 {
       
   186     // FIXME: parse error
       
   187     notImplemented(); // fragment case
       
   188     mergeAttributesFromTokenIntoElement(token, m_openElements.bodyElement());
       
   189 }
       
   190 
       
   191 void HTMLConstructionSite::insertDoctype(AtomicHTMLToken& token)
       
   192 {
       
   193     ASSERT(token.type() == HTMLToken::DOCTYPE);
       
   194     attach(m_document, DocumentType::create(m_document, token.name(), String::adopt(token.publicIdentifier()), String::adopt(token.systemIdentifier())));
       
   195     // FIXME: Move quirks mode detection from DocumentType element to here.
       
   196     notImplemented();
       
   197     if (token.forceQuirks())
       
   198         m_document->setParseMode(Document::Compat);
       
   199 }
       
   200 
       
   201 void HTMLConstructionSite::insertComment(AtomicHTMLToken& token)
       
   202 {
       
   203     ASSERT(token.type() == HTMLToken::Comment);
       
   204     attach(currentElement(), Comment::create(m_document, token.comment()));
       
   205 }
       
   206 
       
   207 void HTMLConstructionSite::insertCommentOnDocument(AtomicHTMLToken& token)
       
   208 {
       
   209     ASSERT(token.type() == HTMLToken::Comment);
       
   210     attach(m_document, Comment::create(m_document, token.comment()));
       
   211 }
       
   212 
       
   213 void HTMLConstructionSite::insertCommentOnHTMLHtmlElement(AtomicHTMLToken& token)
       
   214 {
       
   215     ASSERT(token.type() == HTMLToken::Comment);
       
   216     attach(m_openElements.htmlElement(), Comment::create(m_document, token.comment()));
       
   217 }
       
   218 
       
   219 PassRefPtr<Element> HTMLConstructionSite::attachToCurrent(PassRefPtr<Element> child)
       
   220 {
       
   221     return attach(currentElement(), child);
       
   222 }
       
   223 
       
   224 void HTMLConstructionSite::insertHTMLHtmlElement(AtomicHTMLToken& token)
       
   225 {
       
   226     ASSERT(!shouldFosterParent());
       
   227     m_openElements.pushHTMLHtmlElement(attachToCurrent(createHTMLElement(token)));
       
   228     dispatchDocumentElementAvailableIfNeeded();
       
   229 }
       
   230 
       
   231 void HTMLConstructionSite::insertHTMLHeadElement(AtomicHTMLToken& token)
       
   232 {
       
   233     ASSERT(!shouldFosterParent());
       
   234     m_head = attachToCurrent(createHTMLElement(token));
       
   235     m_openElements.pushHTMLHeadElement(m_head);
       
   236 }
       
   237 
       
   238 void HTMLConstructionSite::insertHTMLBodyElement(AtomicHTMLToken& token)
       
   239 {
       
   240     ASSERT(!shouldFosterParent());
       
   241     m_openElements.pushHTMLBodyElement(attachToCurrent(createHTMLElement(token)));
       
   242 }
       
   243 
       
   244 void HTMLConstructionSite::insertHTMLFormElement(AtomicHTMLToken& token)
       
   245 {
       
   246     insertHTMLElement(token);
       
   247     ASSERT(currentElement()->isHTMLElement());
       
   248     ASSERT(currentElement()->hasTagName(formTag));
       
   249     m_form = static_cast<HTMLFormElement*>(currentElement());
       
   250 }
       
   251 
       
   252 void HTMLConstructionSite::insertHTMLElement(AtomicHTMLToken& token)
       
   253 {
       
   254     m_openElements.push(attachToCurrent(createHTMLElement(token)));
       
   255 }
       
   256 
       
   257 void HTMLConstructionSite::insertSelfClosingHTMLElement(AtomicHTMLToken& token)
       
   258 {
       
   259     ASSERT(token.type() == HTMLToken::StartTag);
       
   260     RefPtr<Element> element = attachToCurrent(createHTMLElement(token));
       
   261     // Normally HTMLElementStack is responsible for calling finishParsingChildren,
       
   262     // but self-closing elements are never in the element stack so the stack
       
   263     // doesn't get a chance to tell them that we're done parsing their children.
       
   264     element->finishParsingChildren();
       
   265     // FIXME: Do we want to acknowledge the token's self-closing flag?
       
   266     // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#acknowledge-self-closing-flag
       
   267 }
       
   268 
       
   269 void HTMLConstructionSite::insertFormattingElement(AtomicHTMLToken& token)
       
   270 {
       
   271     // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#the-stack-of-open-elements
       
   272     // Possible active formatting elements include:
       
   273     // a, b, big, code, em, font, i, nobr, s, small, strike, strong, tt, and u.
       
   274     insertHTMLElement(token);
       
   275     m_activeFormattingElements.append(currentElement());
       
   276 }
       
   277 
       
   278 void HTMLConstructionSite::insertScriptElement(AtomicHTMLToken& token)
       
   279 {
       
   280     RefPtr<HTMLScriptElement> element = HTMLScriptElement::create(scriptTag, m_document, true);
       
   281     element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission);
       
   282     m_openElements.push(attachToCurrent(element.release()));
       
   283 }
       
   284 
       
   285 void HTMLConstructionSite::insertForeignElement(AtomicHTMLToken& token, const AtomicString& namespaceURI)
       
   286 {
       
   287     ASSERT(token.type() == HTMLToken::StartTag);
       
   288     notImplemented(); // parseError when xmlns or xmlns:xlink are wrong.
       
   289 
       
   290     RefPtr<Element> element = attachToCurrent(createElement(token, namespaceURI));
       
   291     if (!token.selfClosing())
       
   292         m_openElements.push(element);
       
   293 }
       
   294 
       
   295 void HTMLConstructionSite::insertTextNode(const String& characters)
       
   296 {
       
   297     AttachmentSite site;
       
   298     site.parent = currentElement();
       
   299     site.nextChild = 0;
       
   300     if (shouldFosterParent())
       
   301         findFosterSite(site);
       
   302 
       
   303     Node* previousChild = site.nextChild ? site.nextChild->previousSibling() : site.parent->lastChild();
       
   304     if (previousChild && previousChild->isTextNode()) {
       
   305         // FIXME: We're only supposed to append to this text node if it
       
   306         // was the last text node inserted by the parser.
       
   307         CharacterData* textNode = static_cast<CharacterData*>(previousChild);
       
   308         textNode->parserAppendData(characters);
       
   309         return;
       
   310     }
       
   311 
       
   312     attachAtSite(site, Text::create(m_document, characters));
       
   313 }
       
   314 
       
   315 PassRefPtr<Element> HTMLConstructionSite::createElement(AtomicHTMLToken& token, const AtomicString& namespaceURI)
       
   316 {
       
   317     QualifiedName tagName(nullAtom, token.name(), namespaceURI);
       
   318     RefPtr<Element> element = m_document->createElement(tagName, true);
       
   319     element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission);
       
   320     return element.release();
       
   321 }
       
   322 
       
   323 PassRefPtr<Element> HTMLConstructionSite::createHTMLElement(AtomicHTMLToken& token)
       
   324 {
       
   325     QualifiedName tagName(nullAtom, token.name(), xhtmlNamespaceURI);
       
   326     // FIXME: This can't use HTMLConstructionSite::createElement because we
       
   327     // have to pass the current form element.  We should rework form association
       
   328     // to occur after construction to allow better code sharing here.
       
   329     RefPtr<Element> element = HTMLElementFactory::createHTMLElement(tagName, m_document, form(), true);
       
   330     element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission);
       
   331     ASSERT(element->isHTMLElement());
       
   332     return element.release();
       
   333 }
       
   334 
       
   335 PassRefPtr<Element> HTMLConstructionSite::createHTMLElementFromElementRecord(HTMLElementStack::ElementRecord* record)
       
   336 {
       
   337     return createHTMLElementFromSavedElement(record->element());
       
   338 }
       
   339 
       
   340 namespace {
       
   341 
       
   342 PassRefPtr<NamedNodeMap> cloneAttributes(Element* element)
       
   343 {
       
   344     NamedNodeMap* attributes = element->attributes(true);
       
   345     if (!attributes)
       
   346         return 0;
       
   347 
       
   348     RefPtr<NamedNodeMap> newAttributes = NamedNodeMap::create();
       
   349     for (size_t i = 0; i < attributes->length(); ++i) {
       
   350         Attribute* attribute = attributes->attributeItem(i);
       
   351         RefPtr<Attribute> clone = Attribute::createMapped(attribute->name(), attribute->value());
       
   352         newAttributes->addAttribute(clone);
       
   353     }
       
   354     return newAttributes.release();
       
   355 }
       
   356 
       
   357 }
       
   358 
       
   359 PassRefPtr<Element> HTMLConstructionSite::createHTMLElementFromSavedElement(Element* element)
       
   360 {
       
   361     // FIXME: This method is wrong.  We should be using the original token.
       
   362     // Using an Element* causes us to fail examples like this:
       
   363     // <b id="1"><p><script>document.getElementById("1").id = "2"</script></p>TEXT</b>
       
   364     // When reconstructTheActiveFormattingElements calls this method to open
       
   365     // a second <b> tag to wrap TEXT, it will have id "2", even though the HTML5
       
   366     // spec implies it should be "1".  Minefield matches the HTML5 spec here.
       
   367 
       
   368     ASSERT(element->isHTMLElement()); // otherwise localName() might be wrong.
       
   369     AtomicHTMLToken fakeToken(HTMLToken::StartTag, element->localName(), cloneAttributes(element));
       
   370     return createHTMLElement(fakeToken);
       
   371 }
       
   372 
       
   373 bool HTMLConstructionSite::indexOfFirstUnopenFormattingElement(unsigned& firstUnopenElementIndex) const
       
   374 {
       
   375     if (m_activeFormattingElements.isEmpty())
       
   376         return false;
       
   377     unsigned index = m_activeFormattingElements.size();
       
   378     do {
       
   379         --index;
       
   380         const HTMLFormattingElementList::Entry& entry = m_activeFormattingElements.at(index);
       
   381         if (entry.isMarker() || m_openElements.contains(entry.element())) {
       
   382             firstUnopenElementIndex = index + 1;
       
   383             return firstUnopenElementIndex < m_activeFormattingElements.size();
       
   384         }
       
   385     } while (index);
       
   386     firstUnopenElementIndex = index;
       
   387     return true;
       
   388 }
       
   389 
       
   390 void HTMLConstructionSite::reconstructTheActiveFormattingElements()
       
   391 {
       
   392     unsigned firstUnopenElementIndex;
       
   393     if (!indexOfFirstUnopenFormattingElement(firstUnopenElementIndex))
       
   394         return;
       
   395 
       
   396     unsigned unopenEntryIndex = firstUnopenElementIndex;
       
   397     ASSERT(unopenEntryIndex < m_activeFormattingElements.size());
       
   398     for (; unopenEntryIndex < m_activeFormattingElements.size(); ++unopenEntryIndex) {
       
   399         HTMLFormattingElementList::Entry& unopenedEntry = m_activeFormattingElements.at(unopenEntryIndex);
       
   400         RefPtr<Element> reconstructed = createHTMLElementFromSavedElement(unopenedEntry.element());
       
   401         m_openElements.push(attachToCurrent(reconstructed.release()));
       
   402         unopenedEntry.replaceElement(currentElement());
       
   403     }
       
   404 }
       
   405 
       
   406 void HTMLConstructionSite::generateImpliedEndTagsWithExclusion(const AtomicString& tagName)
       
   407 {
       
   408     while (hasImpliedEndTag(currentElement()) && !currentElement()->hasLocalName(tagName))
       
   409         m_openElements.pop();
       
   410 }
       
   411 
       
   412 void HTMLConstructionSite::generateImpliedEndTags()
       
   413 {
       
   414     while (hasImpliedEndTag(currentElement()))
       
   415         m_openElements.pop();
       
   416 }
       
   417 
       
   418 void HTMLConstructionSite::findFosterSite(AttachmentSite& site)
       
   419 {
       
   420     HTMLElementStack::ElementRecord* lastTableElementRecord = m_openElements.topmost(tableTag.localName());
       
   421     if (lastTableElementRecord) {
       
   422         Element* lastTableElement = lastTableElementRecord->element();
       
   423         if (Node* parent = lastTableElement->parent()) {
       
   424             site.parent = parent;
       
   425             site.nextChild = lastTableElement;
       
   426             return;
       
   427         }
       
   428         site.parent = lastTableElementRecord->next()->element();
       
   429         site.nextChild = 0;
       
   430         return;
       
   431     }
       
   432     // Fragment case
       
   433     site.parent = m_openElements.bottom(); // <html> element
       
   434     site.nextChild = 0;
       
   435 }
       
   436 
       
   437 bool HTMLConstructionSite::shouldFosterParent() const
       
   438 {
       
   439     return m_redirectAttachToFosterParent
       
   440         && causesFosterParenting(currentElement()->tagQName());
       
   441 }
       
   442 
       
   443 void HTMLConstructionSite::fosterParent(Node* node)
       
   444 {
       
   445     AttachmentSite site;
       
   446     findFosterSite(site);
       
   447     attachAtSite(site, node);
       
   448 }
       
   449 
       
   450 }