Home | History | Annotate | Download | only in html
      1 /*
      2  * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org)
      3  *           (C) 1999 Antti Koivisto (koivisto (at) kde.org)
      4  *           (C) 2000 Stefan Schimanski (1Stein (at) gmx.de)
      5  * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2011 Apple Inc. All rights reserved.
      6  * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
      7  *
      8  * This library is free software; you can redistribute it and/or
      9  * modify it under the terms of the GNU Library General Public
     10  * License as published by the Free Software Foundation; either
     11  * version 2 of the License, or (at your option) any later version.
     12  *
     13  * This library is distributed in the hope that it will be useful,
     14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     16  * Library General Public License for more details.
     17  *
     18  * You should have received a copy of the GNU Library General Public License
     19  * along with this library; see the file COPYING.LIB.  If not, write to
     20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     21  * Boston, MA 02110-1301, USA.
     22  */
     23 
     24 #include "config.h"
     25 #include "core/html/HTMLObjectElement.h"
     26 
     27 #include "bindings/core/v8/ScriptEventListener.h"
     28 #include "core/HTMLNames.h"
     29 #include "core/dom/Attribute.h"
     30 #include "core/dom/ElementTraversal.h"
     31 #include "core/dom/TagCollection.h"
     32 #include "core/dom/Text.h"
     33 #include "core/dom/shadow/ShadowRoot.h"
     34 #include "core/fetch/ImageResource.h"
     35 #include "core/html/FormDataList.h"
     36 #include "core/html/HTMLDocument.h"
     37 #include "core/html/HTMLImageLoader.h"
     38 #include "core/html/HTMLMetaElement.h"
     39 #include "core/html/HTMLParamElement.h"
     40 #include "core/html/parser/HTMLParserIdioms.h"
     41 #include "core/frame/Settings.h"
     42 #include "core/plugins/PluginView.h"
     43 #include "core/rendering/RenderEmbeddedObject.h"
     44 #include "platform/MIMETypeRegistry.h"
     45 #include "platform/Widget.h"
     46 
     47 namespace blink {
     48 
     49 using namespace HTMLNames;
     50 
     51 inline HTMLObjectElement::HTMLObjectElement(Document& document, HTMLFormElement* form, bool createdByParser)
     52     : HTMLPlugInElement(objectTag, document, createdByParser, ShouldNotPreferPlugInsForImages)
     53     , m_useFallbackContent(false)
     54 {
     55     associateByParser(form);
     56 }
     57 
     58 inline HTMLObjectElement::~HTMLObjectElement()
     59 {
     60 #if !ENABLE(OILPAN)
     61     setForm(0);
     62 #endif
     63 }
     64 
     65 PassRefPtrWillBeRawPtr<HTMLObjectElement> HTMLObjectElement::create(Document& document, HTMLFormElement* form, bool createdByParser)
     66 {
     67     RefPtrWillBeRawPtr<HTMLObjectElement> element = adoptRefWillBeNoop(new HTMLObjectElement(document, form, createdByParser));
     68     element->ensureUserAgentShadowRoot();
     69     return element.release();
     70 }
     71 
     72 void HTMLObjectElement::trace(Visitor* visitor)
     73 {
     74     FormAssociatedElement::trace(visitor);
     75     HTMLPlugInElement::trace(visitor);
     76 }
     77 
     78 RenderWidget* HTMLObjectElement::existingRenderWidget() const
     79 {
     80     return renderPart(); // This will return 0 if the renderer is not a RenderPart.
     81 }
     82 
     83 bool HTMLObjectElement::isPresentationAttribute(const QualifiedName& name) const
     84 {
     85     if (name == borderAttr)
     86         return true;
     87     return HTMLPlugInElement::isPresentationAttribute(name);
     88 }
     89 
     90 void HTMLObjectElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style)
     91 {
     92     if (name == borderAttr)
     93         applyBorderAttributeToStyle(value, style);
     94     else
     95         HTMLPlugInElement::collectStyleForPresentationAttribute(name, value, style);
     96 }
     97 
     98 void HTMLObjectElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
     99 {
    100     if (name == formAttr)
    101         formAttributeChanged();
    102     else if (name == typeAttr) {
    103         m_serviceType = value.lower();
    104         size_t pos = m_serviceType.find(";");
    105         if (pos != kNotFound)
    106             m_serviceType = m_serviceType.left(pos);
    107         // FIXME: What is the right thing to do here? Should we supress the
    108         // reload stuff when a persistable widget-type is specified?
    109         reloadPluginOnAttributeChange(name);
    110         if (!renderer())
    111             requestPluginCreationWithoutRendererIfPossible();
    112     } else if (name == dataAttr) {
    113         m_url = stripLeadingAndTrailingHTMLSpaces(value);
    114         if (renderer() && isImageType()) {
    115             setNeedsWidgetUpdate(true);
    116             if (!m_imageLoader)
    117                 m_imageLoader = HTMLImageLoader::create(this);
    118             m_imageLoader->updateFromElement(ImageLoader::UpdateIgnorePreviousError);
    119         } else {
    120             reloadPluginOnAttributeChange(name);
    121         }
    122     } else if (name == classidAttr) {
    123         m_classId = value;
    124         reloadPluginOnAttributeChange(name);
    125     } else {
    126         HTMLPlugInElement::parseAttribute(name, value);
    127     }
    128 }
    129 
    130 static void mapDataParamToSrc(Vector<String>* paramNames, Vector<String>* paramValues)
    131 {
    132     // Some plugins don't understand the "data" attribute of the OBJECT tag (i.e. Real and WMP
    133     // require "src" attribute).
    134     int srcIndex = -1, dataIndex = -1;
    135     for (unsigned i = 0; i < paramNames->size(); ++i) {
    136         if (equalIgnoringCase((*paramNames)[i], "src"))
    137             srcIndex = i;
    138         else if (equalIgnoringCase((*paramNames)[i], "data"))
    139             dataIndex = i;
    140     }
    141 
    142     if (srcIndex == -1 && dataIndex != -1) {
    143         paramNames->append("src");
    144         paramValues->append((*paramValues)[dataIndex]);
    145     }
    146 }
    147 
    148 // FIXME: This function should not deal with url or serviceType!
    149 void HTMLObjectElement::parametersForPlugin(Vector<String>& paramNames, Vector<String>& paramValues, String& url, String& serviceType)
    150 {
    151     HashSet<StringImpl*, CaseFoldingHash> uniqueParamNames;
    152     String urlParameter;
    153 
    154     // Scan the PARAM children and store their name/value pairs.
    155     // Get the URL and type from the params if we don't already have them.
    156     for (HTMLParamElement* p = Traversal<HTMLParamElement>::firstChild(*this); p; p = Traversal<HTMLParamElement>::nextSibling(*p)) {
    157         String name = p->name();
    158         if (name.isEmpty())
    159             continue;
    160 
    161         uniqueParamNames.add(name.impl());
    162         paramNames.append(p->name());
    163         paramValues.append(p->value());
    164 
    165         // FIXME: url adjustment does not belong in this function.
    166         if (url.isEmpty() && urlParameter.isEmpty() && (equalIgnoringCase(name, "src") || equalIgnoringCase(name, "movie") || equalIgnoringCase(name, "code") || equalIgnoringCase(name, "url")))
    167             urlParameter = stripLeadingAndTrailingHTMLSpaces(p->value());
    168         // FIXME: serviceType calculation does not belong in this function.
    169         if (serviceType.isEmpty() && equalIgnoringCase(name, "type")) {
    170             serviceType = p->value();
    171             size_t pos = serviceType.find(";");
    172             if (pos != kNotFound)
    173                 serviceType = serviceType.left(pos);
    174         }
    175     }
    176 
    177     // When OBJECT is used for an applet via Sun's Java plugin, the CODEBASE attribute in the tag
    178     // points to the Java plugin itself (an ActiveX component) while the actual applet CODEBASE is
    179     // in a PARAM tag. See <http://java.sun.com/products/plugin/1.2/docs/tags.html>. This means
    180     // we have to explicitly suppress the tag's CODEBASE attribute if there is none in a PARAM,
    181     // else our Java plugin will misinterpret it. [4004531]
    182     String codebase;
    183     if (MIMETypeRegistry::isJavaAppletMIMEType(serviceType)) {
    184         codebase = "codebase";
    185         uniqueParamNames.add(codebase.impl()); // pretend we found it in a PARAM already
    186     }
    187 
    188     // Turn the attributes of the <object> element into arrays, but don't override <param> values.
    189     AttributeCollection attributes = this->attributes();
    190     AttributeCollection::iterator end = attributes.end();
    191     for (AttributeCollection::iterator it = attributes.begin(); it != end; ++it) {
    192         const AtomicString& name = it->name().localName();
    193         if (!uniqueParamNames.contains(name.impl())) {
    194             paramNames.append(name.string());
    195             paramValues.append(it->value().string());
    196         }
    197     }
    198 
    199     mapDataParamToSrc(&paramNames, &paramValues);
    200 
    201     // HTML5 says that an object resource's URL is specified by the object's data
    202     // attribute, not by a param element. However, for compatibility, allow the
    203     // resource's URL to be given by a param named "src", "movie", "code" or "url"
    204     // if we know that resource points to a plug-in.
    205     if (url.isEmpty() && !urlParameter.isEmpty()) {
    206         KURL completedURL = document().completeURL(urlParameter);
    207         bool useFallback;
    208         if (shouldUsePlugin(completedURL, serviceType, false, useFallback))
    209             url = urlParameter;
    210     }
    211 }
    212 
    213 
    214 bool HTMLObjectElement::hasFallbackContent() const
    215 {
    216     for (Node* child = firstChild(); child; child = child->nextSibling()) {
    217         // Ignore whitespace-only text, and <param> tags, any other content is fallback content.
    218         if (child->isTextNode()) {
    219             if (!toText(child)->containsOnlyWhitespace())
    220                 return true;
    221         } else if (!isHTMLParamElement(*child)) {
    222             return true;
    223         }
    224     }
    225     return false;
    226 }
    227 
    228 bool HTMLObjectElement::hasValidClassId()
    229 {
    230     if (MIMETypeRegistry::isJavaAppletMIMEType(m_serviceType) && classId().startsWith("java:", false))
    231         return true;
    232 
    233     // HTML5 says that fallback content should be rendered if a non-empty
    234     // classid is specified for which the UA can't find a suitable plug-in.
    235     return classId().isEmpty();
    236 }
    237 
    238 void HTMLObjectElement::reloadPluginOnAttributeChange(const QualifiedName& name)
    239 {
    240     // Following,
    241     //   http://www.whatwg.org/specs/web-apps/current-work/#the-object-element
    242     //   (Enumerated list below "Whenever one of the following conditions occur:")
    243     //
    244     // the updating of certain attributes should bring about "redetermination"
    245     // of what the element contains.
    246     bool needsInvalidation;
    247     if (name == typeAttr) {
    248         needsInvalidation = !fastHasAttribute(classidAttr) && !fastHasAttribute(dataAttr);
    249     } else if (name == dataAttr) {
    250         needsInvalidation = !fastHasAttribute(classidAttr);
    251     } else if (name == classidAttr) {
    252         needsInvalidation = true;
    253     } else {
    254         ASSERT_NOT_REACHED();
    255         needsInvalidation = false;
    256     }
    257     setNeedsWidgetUpdate(true);
    258     if (needsInvalidation)
    259         setNeedsStyleRecalc(SubtreeStyleChange);
    260 }
    261 
    262 // FIXME: This should be unified with HTMLEmbedElement::updateWidget and
    263 // moved down into HTMLPluginElement.cpp
    264 void HTMLObjectElement::updateWidgetInternal()
    265 {
    266     ASSERT(!renderEmbeddedObject()->showsUnavailablePluginIndicator());
    267     ASSERT(needsWidgetUpdate());
    268     setNeedsWidgetUpdate(false);
    269     // FIXME: This should ASSERT isFinishedParsingChildren() instead.
    270     if (!isFinishedParsingChildren()) {
    271         dispatchErrorEvent();
    272         return;
    273     }
    274 
    275     // FIXME: I'm not sure it's ever possible to get into updateWidget during a
    276     // removal, but just in case we should avoid loading the frame to prevent
    277     // security bugs.
    278     if (!SubframeLoadingDisabler::canLoadFrame(*this)) {
    279         dispatchErrorEvent();
    280         return;
    281     }
    282 
    283     String url = this->url();
    284     String serviceType = m_serviceType;
    285 
    286     // FIXME: These should be joined into a PluginParameters class.
    287     Vector<String> paramNames;
    288     Vector<String> paramValues;
    289     parametersForPlugin(paramNames, paramValues, url, serviceType);
    290 
    291     // Note: url is modified above by parametersForPlugin.
    292     if (!allowedToLoadFrameURL(url)) {
    293         dispatchErrorEvent();
    294         return;
    295     }
    296 
    297     // FIXME: Is it possible to get here without a renderer now that we don't have beforeload events?
    298     if (!renderer())
    299         return;
    300 
    301     if (!hasValidClassId() || !requestObject(url, serviceType, paramNames, paramValues)) {
    302         if (!url.isEmpty())
    303             dispatchErrorEvent();
    304         if (hasFallbackContent())
    305             renderFallbackContent();
    306     }
    307 }
    308 
    309 bool HTMLObjectElement::rendererIsNeeded(const RenderStyle& style)
    310 {
    311     // FIXME: This check should not be needed, detached documents never render!
    312     if (!document().frame())
    313         return false;
    314     return HTMLPlugInElement::rendererIsNeeded(style);
    315 }
    316 
    317 Node::InsertionNotificationRequest HTMLObjectElement::insertedInto(ContainerNode* insertionPoint)
    318 {
    319     HTMLPlugInElement::insertedInto(insertionPoint);
    320     FormAssociatedElement::insertedInto(insertionPoint);
    321     return InsertionDone;
    322 }
    323 
    324 void HTMLObjectElement::removedFrom(ContainerNode* insertionPoint)
    325 {
    326     HTMLPlugInElement::removedFrom(insertionPoint);
    327     FormAssociatedElement::removedFrom(insertionPoint);
    328 }
    329 
    330 void HTMLObjectElement::childrenChanged(const ChildrenChange& change)
    331 {
    332     if (inDocument() && !useFallbackContent()) {
    333         setNeedsWidgetUpdate(true);
    334         setNeedsStyleRecalc(SubtreeStyleChange);
    335     }
    336     HTMLPlugInElement::childrenChanged(change);
    337 }
    338 
    339 bool HTMLObjectElement::isURLAttribute(const Attribute& attribute) const
    340 {
    341     return attribute.name() == codebaseAttr || attribute.name() == dataAttr
    342         || (attribute.name() == usemapAttr && attribute.value()[0] != '#')
    343         || HTMLPlugInElement::isURLAttribute(attribute);
    344 }
    345 
    346 bool HTMLObjectElement::hasLegalLinkAttribute(const QualifiedName& name) const
    347 {
    348     return name == classidAttr || name == dataAttr || name == codebaseAttr || HTMLPlugInElement::hasLegalLinkAttribute(name);
    349 }
    350 
    351 const QualifiedName& HTMLObjectElement::subResourceAttributeName() const
    352 {
    353     return dataAttr;
    354 }
    355 
    356 const AtomicString HTMLObjectElement::imageSourceURL() const
    357 {
    358     return getAttribute(dataAttr);
    359 }
    360 
    361 // FIXME: Remove this hack.
    362 void HTMLObjectElement::reattachFallbackContent()
    363 {
    364     // This can happen inside of attach() in the middle of a recalcStyle so we need to
    365     // reattach synchronously here.
    366     if (document().inStyleRecalc())
    367         reattach();
    368     else
    369         lazyReattachIfAttached();
    370 }
    371 
    372 void HTMLObjectElement::renderFallbackContent()
    373 {
    374     if (useFallbackContent())
    375         return;
    376 
    377     if (!inDocument())
    378         return;
    379 
    380     // Before we give up and use fallback content, check to see if this is a MIME type issue.
    381     if (m_imageLoader && m_imageLoader->image() && m_imageLoader->image()->status() != Resource::LoadError) {
    382         m_serviceType = m_imageLoader->image()->response().mimeType();
    383         if (!isImageType()) {
    384             // If we don't think we have an image type anymore, then clear the image from the loader.
    385             m_imageLoader->setImage(0);
    386             reattachFallbackContent();
    387             return;
    388         }
    389     }
    390 
    391     m_useFallbackContent = true;
    392 
    393     // FIXME: Style gets recalculated which is suboptimal.
    394     reattachFallbackContent();
    395 }
    396 
    397 bool HTMLObjectElement::isExposed() const
    398 {
    399     // http://www.whatwg.org/specs/web-apps/current-work/#exposed
    400     for (HTMLObjectElement* ancestor = Traversal<HTMLObjectElement>::firstAncestor(*this); ancestor; ancestor = Traversal<HTMLObjectElement>::firstAncestor(*ancestor)) {
    401         if (ancestor->isExposed())
    402             return false;
    403     }
    404     for (HTMLElement* element = Traversal<HTMLElement>::firstWithin(*this); element; element = Traversal<HTMLElement>::next(*element, this)) {
    405         if (isHTMLObjectElement(*element) || isHTMLEmbedElement(*element))
    406             return false;
    407     }
    408     return true;
    409 }
    410 
    411 bool HTMLObjectElement::containsJavaApplet() const
    412 {
    413     if (MIMETypeRegistry::isJavaAppletMIMEType(getAttribute(typeAttr)))
    414         return true;
    415 
    416     for (HTMLElement* child = Traversal<HTMLElement>::firstChild(*this); child; child = Traversal<HTMLElement>::nextSibling(*child)) {
    417         if (isHTMLParamElement(*child)
    418                 && equalIgnoringCase(child->getNameAttribute(), "type")
    419                 && MIMETypeRegistry::isJavaAppletMIMEType(child->getAttribute(valueAttr).string()))
    420             return true;
    421         if (isHTMLObjectElement(*child) && toHTMLObjectElement(*child).containsJavaApplet())
    422             return true;
    423         if (isHTMLAppletElement(*child))
    424             return true;
    425     }
    426 
    427     return false;
    428 }
    429 
    430 void HTMLObjectElement::didMoveToNewDocument(Document& oldDocument)
    431 {
    432     FormAssociatedElement::didMoveToNewDocument(oldDocument);
    433     HTMLPlugInElement::didMoveToNewDocument(oldDocument);
    434 }
    435 
    436 bool HTMLObjectElement::appendFormData(FormDataList& encoding, bool)
    437 {
    438     if (name().isEmpty())
    439         return false;
    440 
    441     Widget* widget = pluginWidget();
    442     if (!widget || !widget->isPluginView())
    443         return false;
    444     String value;
    445     if (!toPluginView(widget)->getFormValue(value))
    446         return false;
    447     encoding.appendData(name(), value);
    448     return true;
    449 }
    450 
    451 HTMLFormElement* HTMLObjectElement::formOwner() const
    452 {
    453     return FormAssociatedElement::form();
    454 }
    455 
    456 bool HTMLObjectElement::isInteractiveContent() const
    457 {
    458     return fastHasAttribute(usemapAttr);
    459 }
    460 
    461 bool HTMLObjectElement::useFallbackContent() const
    462 {
    463     return HTMLPlugInElement::useFallbackContent() || m_useFallbackContent;
    464 }
    465 
    466 }
    467