diff options
Diffstat (limited to 'Source/WebCore/html/HTMLObjectElement.cpp')
-rw-r--r-- | Source/WebCore/html/HTMLObjectElement.cpp | 225 |
1 files changed, 116 insertions, 109 deletions
diff --git a/Source/WebCore/html/HTMLObjectElement.cpp b/Source/WebCore/html/HTMLObjectElement.cpp index 222f99d43..1a49f0899 100644 --- a/Source/WebCore/html/HTMLObjectElement.cpp +++ b/Source/WebCore/html/HTMLObjectElement.cpp @@ -2,7 +2,7 @@ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2000 Stefan Schimanski (1Stein@gmx.de) - * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2004-2017 Apple Inc. All rights reserved. * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) * * This library is free software; you can redistribute it and/or @@ -27,11 +27,7 @@ #include "Attribute.h" #include "CSSValueKeywords.h" #include "CachedImage.h" -#include "Chrome.h" -#include "ChromeClient.h" #include "ElementIterator.h" -#include "EventNames.h" -#include "ExceptionCode.h" #include "FormDataList.h" #include "Frame.h" #include "HTMLDocument.h" @@ -55,8 +51,8 @@ #include <wtf/Ref.h> #if PLATFORM(IOS) -#include "RuntimeApplicationChecksIOS.h" -#include "WebCoreSystemInterface.h" +#include "RuntimeApplicationChecks.h" +#include <wtf/spi/darwin/dyldSPI.h> #endif namespace WebCore { @@ -64,29 +60,29 @@ namespace WebCore { using namespace HTMLNames; inline HTMLObjectElement::HTMLObjectElement(const QualifiedName& tagName, Document& document, HTMLFormElement* form, bool createdByParser) - : HTMLPlugInImageElement(tagName, document, createdByParser, ShouldNotPreferPlugInsForImages) + : HTMLPlugInImageElement(tagName, document, createdByParser) + , FormAssociatedElement(form) , m_docNamedItem(true) , m_useFallbackContent(false) { ASSERT(hasTagName(objectTag)); - setForm(form ? form : HTMLFormElement::findClosestFormAncestor(*this)); } inline HTMLObjectElement::~HTMLObjectElement() { } -PassRefPtr<HTMLObjectElement> HTMLObjectElement::create(const QualifiedName& tagName, Document& document, HTMLFormElement* form, bool createdByParser) +Ref<HTMLObjectElement> HTMLObjectElement::create(const QualifiedName& tagName, Document& document, HTMLFormElement* form, bool createdByParser) { - return adoptRef(new HTMLObjectElement(tagName, document, form, createdByParser)); + return adoptRef(*new HTMLObjectElement(tagName, document, form, createdByParser)); } -RenderWidget* HTMLObjectElement::renderWidgetForJSBindings() const +RenderWidget* HTMLObjectElement::renderWidgetLoadingPlugin() const { // Needs to load the plugin immediatedly because this function is called // when JavaScript code accesses the plugin. // FIXME: <rdar://16893708> Check if dispatching events here is safe. - document().updateLayoutIgnorePendingStylesheets(Document::RunPostLayoutTasksSynchronously); + document().updateLayoutIgnorePendingStylesheets(Document::RunPostLayoutTasks::Synchronously); return renderWidget(); // This will return 0 if the renderer is not a RenderWidget. } @@ -107,58 +103,58 @@ void HTMLObjectElement::collectStyleForPresentationAttribute(const QualifiedName void HTMLObjectElement::parseAttribute(const QualifiedName& name, const AtomicString& value) { + bool invalidateRenderer = false; + if (name == formAttr) formAttributeChanged(); else if (name == typeAttr) { - m_serviceType = value.lower(); - size_t pos = m_serviceType.find(";"); - if (pos != notFound) - m_serviceType = m_serviceType.left(pos); - if (renderer()) - setNeedsWidgetUpdate(true); + m_serviceType = value.string().left(value.find(';')).convertToASCIILowercase(); + invalidateRenderer = !hasAttributeWithoutSynchronization(classidAttr); + setNeedsWidgetUpdate(true); } else if (name == dataAttr) { m_url = stripLeadingAndTrailingHTMLSpaces(value); document().updateStyleIfNeeded(); - if (renderer()) { - setNeedsWidgetUpdate(true); - if (isImageType()) { - if (!m_imageLoader) - m_imageLoader = adoptPtr(new HTMLImageLoader(this)); - m_imageLoader->updateFromElementIgnoringPreviousError(); - } + if (isImageType() && renderer()) { + if (!m_imageLoader) + m_imageLoader = std::make_unique<HTMLImageLoader>(*this); + m_imageLoader->updateFromElementIgnoringPreviousError(); } + invalidateRenderer = !hasAttributeWithoutSynchronization(classidAttr); + setNeedsWidgetUpdate(true); } else if (name == classidAttr) { - m_classId = value; - if (renderer()) - setNeedsWidgetUpdate(true); - } else if (name == onbeforeloadAttr) - setAttributeEventListener(eventNames().beforeloadEvent, name, value); - else + invalidateRenderer = true; + setNeedsWidgetUpdate(true); + } else HTMLPlugInImageElement::parseAttribute(name, value); + + if (!invalidateRenderer || !isConnected() || !renderer()) + return; + + clearUseFallbackContent(); + invalidateStyleAndRenderersForSubtree(); } -static void mapDataParamToSrc(Vector<String>* paramNames, Vector<String>* paramValues) +static void mapDataParamToSrc(Vector<String>& paramNames, Vector<String>& paramValues) { - // Some plugins don't understand the "data" attribute of the OBJECT tag (i.e. Real and WMP - // require "src" attribute). - int srcIndex = -1, dataIndex = -1; - for (unsigned int i = 0; i < paramNames->size(); ++i) { - if (equalIgnoringCase((*paramNames)[i], "src")) - srcIndex = i; - else if (equalIgnoringCase((*paramNames)[i], "data")) - dataIndex = i; + // Some plugins don't understand the "data" attribute of the OBJECT tag (i.e. Real and WMP require "src" attribute). + bool foundSrcParam = false; + String dataParamValue; + for (unsigned i = 0; i < paramNames.size(); ++i) { + if (equalLettersIgnoringASCIICase(paramNames[i], "src")) + foundSrcParam = true; + else if (equalLettersIgnoringASCIICase(paramNames[i], "data")) + dataParamValue = paramValues[i]; } - - if (srcIndex == -1 && dataIndex != -1) { - paramNames->append("src"); - paramValues->append((*paramValues)[dataIndex]); + if (!foundSrcParam && !dataParamValue.isNull()) { + paramNames.append(ASCIILiteral("src")); + paramValues.append(WTFMove(dataParamValue)); } } #if PLATFORM(IOS) static bool shouldNotPerformURLAdjustment() { - static bool shouldNotPerformURLAdjustment = applicationIsNASAHD() && !iosExecutableWasLinkedOnOrAfterVersion(wkIOSSystemVersion_5_0); + static bool shouldNotPerformURLAdjustment = IOSApplication::isNASAHD() && dyld_get_program_sdk_version() < DYLD_IOS_VERSION_5_0; return shouldNotPerformURLAdjustment; } #endif @@ -166,7 +162,7 @@ static bool shouldNotPerformURLAdjustment() // FIXME: This function should not deal with url or serviceType! void HTMLObjectElement::parametersForPlugin(Vector<String>& paramNames, Vector<String>& paramValues, String& url, String& serviceType) { - HashSet<StringImpl*, CaseFoldingHash> uniqueParamNames; + HashSet<StringImpl*, ASCIICaseInsensitiveHash> uniqueParamNames; String urlParameter; // Scan the PARAM children and store their name/value pairs. @@ -181,12 +177,12 @@ void HTMLObjectElement::parametersForPlugin(Vector<String>& paramNames, Vector<S paramValues.append(param.value()); // FIXME: url adjustment does not belong in this function. - if (url.isEmpty() && urlParameter.isEmpty() && (equalIgnoringCase(name, "src") || equalIgnoringCase(name, "movie") || equalIgnoringCase(name, "code") || equalIgnoringCase(name, "url"))) + if (url.isEmpty() && urlParameter.isEmpty() && (equalLettersIgnoringASCIICase(name, "src") || equalLettersIgnoringASCIICase(name, "movie") || equalLettersIgnoringASCIICase(name, "code") || equalLettersIgnoringASCIICase(name, "url"))) urlParameter = stripLeadingAndTrailingHTMLSpaces(param.value()); // FIXME: serviceType calculation does not belong in this function. - if (serviceType.isEmpty() && equalIgnoringCase(name, "type")) { + if (serviceType.isEmpty() && equalLettersIgnoringASCIICase(name, "type")) { serviceType = param.value(); - size_t pos = serviceType.find(";"); + size_t pos = serviceType.find(';'); if (pos != notFound) serviceType = serviceType.left(pos); } @@ -214,7 +210,7 @@ void HTMLObjectElement::parametersForPlugin(Vector<String>& paramNames, Vector<S } } - mapDataParamToSrc(¶mNames, ¶mValues); + mapDataParamToSrc(paramNames, paramValues); // HTML5 says that an object resource's URL is specified by the object's data // attribute, not by a param element. However, for compatibility, allow the @@ -227,7 +223,7 @@ void HTMLObjectElement::parametersForPlugin(Vector<String>& paramNames, Vector<S if (url.isEmpty() && !urlParameter.isEmpty()) { SubframeLoader& loader = document().frame()->loader().subframeLoader(); - if (loader.resourceWillUsePlugin(urlParameter, serviceType, shouldPreferPlugInsForImages())) + if (loader.resourceWillUsePlugin(urlParameter, serviceType)) url = urlParameter; } } @@ -237,10 +233,10 @@ bool HTMLObjectElement::hasFallbackContent() const { for (Node* child = firstChild(); child; child = child->nextSibling()) { // Ignore whitespace-only text, and <param> tags, any other content is fallback content. - if (child->isTextNode()) { - if (!toText(child)->containsOnlyWhitespace()) + if (is<Text>(*child)) { + if (!downcast<Text>(*child).containsOnlyWhitespace()) return true; - } else if (!child->hasTagName(paramTag)) + } else if (!is<HTMLParamElement>(*child)) return true; } return false; @@ -254,26 +250,24 @@ bool HTMLObjectElement::shouldAllowQuickTimeClassIdQuirk() // 'generator' meta tag is present. Only apply this quirk if there is no // fallback content, which ensures the quirk will disable itself if Wiki // Server is updated to generate an alternate embed tag as fallback content. + if (!document().page() || !document().page()->settings().needsSiteSpecificQuirks() || hasFallbackContent() - || !equalIgnoringCase(classId(), "clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B")) + || !equalLettersIgnoringASCIICase(attributeWithoutSynchronization(classidAttr), "clsid:02bf25d5-8c17-4b23-bc80-d3488abddc6b")) return false; - RefPtr<NodeList> metaElements = document().getElementsByTagName(HTMLNames::metaTag.localName()); - unsigned length = metaElements->length(); - for (unsigned i = 0; i < length; ++i) { - HTMLMetaElement& metaElement = toHTMLMetaElement(*metaElements->item(i)); - if (equalIgnoringCase(metaElement.name(), "generator") && metaElement.content().startsWith("Mac OS X Server Web Services Server", false)) + for (auto& metaElement : descendantsOfType<HTMLMetaElement>(document())) { + if (equalLettersIgnoringASCIICase(metaElement.name(), "generator") && metaElement.content().startsWith("Mac OS X Server Web Services Server", false)) return true; } - + return false; } bool HTMLObjectElement::hasValidClassId() { - if (MIMETypeRegistry::isJavaAppletMIMEType(serviceType()) && classId().startsWith("java:", false)) + if (MIMETypeRegistry::isJavaAppletMIMEType(serviceType()) && attributeWithoutSynchronization(classidAttr).startsWith("java:", false)) return true; if (shouldAllowQuickTimeClassIdQuirk()) @@ -281,12 +275,12 @@ bool HTMLObjectElement::hasValidClassId() // HTML5 says that fallback content should be rendered if a non-empty // classid is specified for which the UA can't find a suitable plug-in. - return classId().isEmpty(); + return attributeWithoutSynchronization(classidAttr).isEmpty(); } // FIXME: This should be unified with HTMLEmbedElement::updateWidget and // moved down into HTMLPluginImageElement.cpp -void HTMLObjectElement::updateWidget(PluginCreationOption pluginCreationOption) +void HTMLObjectElement::updateWidget(CreatePlugins createPlugins) { ASSERT(!renderEmbeddedObject()->isPluginUnavailable()); ASSERT(needsWidgetUpdate()); @@ -316,18 +310,18 @@ void HTMLObjectElement::updateWidget(PluginCreationOption pluginCreationOption) // FIXME: It's sadness that we have this special case here. // See http://trac.webkit.org/changeset/25128 and // plugins/netscape-plugin-setwindow-size.html - if (pluginCreationOption == CreateOnlyNonNetscapePlugins && wouldLoadAsNetscapePlugin(url, serviceType)) { + if (createPlugins == CreatePlugins::No && wouldLoadAsPlugIn(url, serviceType)) { // Ensure updateWidget() is called again during layout to create the Netscape plug-in. setNeedsWidgetUpdate(true); return; } - Ref<HTMLObjectElement> protect(*this); // beforeload and plugin loading can make arbitrary DOM mutations. + Ref<HTMLObjectElement> protectedThis(*this); // beforeload and plugin loading can make arbitrary DOM mutations. bool beforeLoadAllowedLoad = guardedDispatchBeforeLoadEvent(url); if (!renderer()) // Do not load the plugin if beforeload removed this element or its renderer. return; - bool success = beforeLoadAllowedLoad && hasValidClassId(); + bool success = beforeLoadAllowedLoad && hasValidClassId() && allowedToLoadFrameURL(url); if (success) success = requestObject(url, serviceType, paramNames, paramValues); if (!success && hasFallbackContent()) @@ -338,7 +332,12 @@ Node::InsertionNotificationRequest HTMLObjectElement::insertedInto(ContainerNode { HTMLPlugInImageElement::insertedInto(insertionPoint); FormAssociatedElement::insertedInto(insertionPoint); - return InsertionDone; + return InsertionShouldCallFinishedInsertingSubtree; +} + +void HTMLObjectElement::finishedInsertingSubtree() +{ + resetFormOwner(); } void HTMLObjectElement::removedFrom(ContainerNode& insertionPoint) @@ -350,21 +349,21 @@ void HTMLObjectElement::removedFrom(ContainerNode& insertionPoint) void HTMLObjectElement::childrenChanged(const ChildChange& change) { updateDocNamedItem(); - if (inDocument() && !useFallbackContent()) { + if (isConnected() && !useFallbackContent()) { setNeedsWidgetUpdate(true); - setNeedsStyleRecalc(); + invalidateStyleForSubtree(); } HTMLPlugInImageElement::childrenChanged(change); } bool HTMLObjectElement::isURLAttribute(const Attribute& attribute) const { - return attribute.name() == dataAttr || (attribute.name() == usemapAttr && attribute.value().string()[0] != '#') || HTMLPlugInImageElement::isURLAttribute(attribute); + return attribute.name() == dataAttr || attribute.name() == codebaseAttr || (attribute.name() == usemapAttr && attribute.value().string()[0] != '#') || HTMLPlugInImageElement::isURLAttribute(attribute); } const AtomicString& HTMLObjectElement::imageSourceURL() const { - return getAttribute(dataAttr); + return attributeWithoutSynchronization(dataAttr); } void HTMLObjectElement::renderFallbackContent() @@ -372,35 +371,37 @@ void HTMLObjectElement::renderFallbackContent() if (useFallbackContent()) return; - if (!inDocument()) + if (!isConnected()) return; - setNeedsStyleRecalc(ReconstructRenderTree); + invalidateStyleAndRenderersForSubtree(); // Before we give up and use fallback content, check to see if this is a MIME type issue. - if (m_imageLoader && m_imageLoader->image() && m_imageLoader->image()->status() != CachedResource::LoadError) { - m_serviceType = m_imageLoader->image()->response().mimeType(); + auto* loader = imageLoader(); + if (loader && loader->image() && loader->image()->status() != CachedResource::LoadError) { + m_serviceType = loader->image()->response().mimeType(); if (!isImageType()) { // If we don't think we have an image type anymore, then clear the image from the loader. - m_imageLoader->setImage(0); + loader->clearImage(); return; } } m_useFallbackContent = true; - // This is here mainly to keep acid2 non-flaky. A style recalc is required to make fallback resources to load. Without forcing - // this may happen after all the other resources have been loaded and the document is already considered complete. - // FIXME: Disentangle fallback content handling from style recalcs. + // This was added to keep Acid 2 non-flaky. A style recalc is required to make fallback resources load. + // Without forcing, this may happen after all the other resources have been loaded and the document is already + // considered complete. FIXME: Would be better to address this with incrementLoadEventDelayCount instead + // or disentangle loading from style entirely. document().updateStyleIfNeeded(); } // FIXME: This should be removed, all callers are almost certainly wrong. static bool isRecognizedTagName(const QualifiedName& tagName) { - DEFINE_STATIC_LOCAL(HashSet<AtomicStringImpl*>, tagList, ()); - if (tagList.isEmpty()) { - const QualifiedName* const * tags = HTMLNames::getHTMLTags(); + static NeverDestroyed<HashSet<AtomicStringImpl*>> tagList; + if (tagList.get().isEmpty()) { + auto* tags = HTMLNames::getHTMLTags(); for (size_t i = 0; i < HTMLNames::HTMLTagsCount; i++) { if (*tags[i] == bgsoundTag || *tags[i] == commandTag @@ -414,10 +415,10 @@ static bool isRecognizedTagName(const QualifiedName& tagName) // because that changes how we parse documents. continue; } - tagList.add(tags[i]->localName().impl()); + tagList.get().add(tags[i]->localName().impl()); } } - return tagList.contains(tagName.localName().impl()); + return tagList.get().contains(tagName.localName().impl()); } void HTMLObjectElement::updateDocNamedItem() @@ -429,35 +430,35 @@ void HTMLObjectElement::updateDocNamedItem() bool isNamedItem = true; Node* child = firstChild(); while (child && isNamedItem) { - if (child->isElementNode()) { - Element* element = toElement(child); + if (is<Element>(*child)) { + Element& element = downcast<Element>(*child); // FIXME: Use of isRecognizedTagName is almost certainly wrong here. - if (isRecognizedTagName(element->tagQName()) && !element->hasTagName(paramTag)) + if (isRecognizedTagName(element.tagQName()) && !element.hasTagName(paramTag)) isNamedItem = false; - } else if (child->isTextNode()) { - if (!toText(child)->containsOnlyWhitespace()) + } else if (is<Text>(*child)) { + if (!downcast<Text>(*child).containsOnlyWhitespace()) isNamedItem = false; } else isNamedItem = false; child = child->nextSibling(); } - if (isNamedItem != wasNamedItem && inDocument() && document().isHTMLDocument()) { - HTMLDocument* document = toHTMLDocument(&this->document()); + if (isNamedItem != wasNamedItem && isConnected() && !isInShadowTree() && is<HTMLDocument>(document())) { + HTMLDocument& document = downcast<HTMLDocument>(this->document()); const AtomicString& id = getIdAttribute(); if (!id.isEmpty()) { if (isNamedItem) - document->addDocumentNamedItem(*id.impl(), *this); + document.addDocumentNamedItem(*id.impl(), *this); else - document->removeDocumentNamedItem(*id.impl(), *this); + document.removeDocumentNamedItem(*id.impl(), *this); } const AtomicString& name = getNameAttribute(); if (!name.isEmpty() && id != name) { if (isNamedItem) - document->addDocumentNamedItem(*name.impl(), *this); + document.addDocumentNamedItem(*name.impl(), *this); else - document->removeDocumentNamedItem(*name.impl(), *this); + document.removeDocumentNamedItem(*name.impl(), *this); } } m_docNamedItem = isNamedItem; @@ -465,14 +466,14 @@ void HTMLObjectElement::updateDocNamedItem() bool HTMLObjectElement::containsJavaApplet() const { - if (MIMETypeRegistry::isJavaAppletMIMEType(getAttribute(typeAttr))) + if (MIMETypeRegistry::isJavaAppletMIMEType(attributeWithoutSynchronization(typeAttr))) return true; for (auto& child : childrenOfType<Element>(*this)) { - if (child.hasTagName(paramTag) && equalIgnoringCase(child.getNameAttribute(), "type") - && MIMETypeRegistry::isJavaAppletMIMEType(child.getAttribute(valueAttr).string())) + if (child.hasTagName(paramTag) && equalLettersIgnoringASCIICase(child.getNameAttribute(), "type") + && MIMETypeRegistry::isJavaAppletMIMEType(child.attributeWithoutSynchronization(valueAttr).string())) return true; - if (child.hasTagName(objectTag) && toHTMLObjectElement(child).containsJavaApplet()) + if (child.hasTagName(objectTag) && downcast<HTMLObjectElement>(child).containsJavaApplet()) return true; if (child.hasTagName(appletTag)) return true; @@ -485,16 +486,16 @@ void HTMLObjectElement::addSubresourceAttributeURLs(ListHashSet<URL>& urls) cons { HTMLPlugInImageElement::addSubresourceAttributeURLs(urls); - addSubresourceURL(urls, document().completeURL(getAttribute(dataAttr))); + addSubresourceURL(urls, document().completeURL(attributeWithoutSynchronization(dataAttr))); // FIXME: Passing a string that starts with "#" to the completeURL function does // not seem like it would work. The image element has similar but not identical code. - const AtomicString& useMap = getAttribute(usemapAttr); + const AtomicString& useMap = attributeWithoutSynchronization(usemapAttr); if (useMap.startsWith('#')) addSubresourceURL(urls, document().completeURL(useMap)); } -void HTMLObjectElement::didMoveToNewDocument(Document* oldDocument) +void HTMLObjectElement::didMoveToNewDocument(Document& oldDocument) { FormAssociatedElement::didMoveToNewDocument(oldDocument); HTMLPlugInImageElement::didMoveToNewDocument(oldDocument); @@ -505,19 +506,25 @@ bool HTMLObjectElement::appendFormData(FormDataList& encoding, bool) if (name().isEmpty()) return false; - Widget* widget = pluginWidget(); - if (!widget || !widget->isPluginViewBase()) + // Use PluginLoadingPolicy::DoNotLoad here or it would fire JS events synchronously + // which would not be safe here. + auto* widget = pluginWidget(PluginLoadingPolicy::DoNotLoad); + if (!is<PluginViewBase>(widget)) return false; String value; - if (!toPluginViewBase(widget)->getFormValue(value)) + if (!downcast<PluginViewBase>(*widget).getFormValue(value)) return false; encoding.appendData(name(), value); return true; } -HTMLFormElement* HTMLObjectElement::virtualForm() const +bool HTMLObjectElement::canContainRangeEndPoint() const { - return FormAssociatedElement::form(); + // Call through to HTMLElement because we need to skip HTMLPlugInElement + // when calling through to the derived class since returns false unconditionally. + // An object element with fallback content should basically be treated like + // a generic HTML element. + return m_useFallbackContent && HTMLElement::canContainRangeEndPoint(); } } |