2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2000 Stefan Schimanski (1Stein@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)
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.
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.
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.
25 #include "HTMLObjectElement.h"
27 #include "Attribute.h"
28 #include "CSSValueKeywords.h"
29 #include "EventNames.h"
30 #include "ExceptionCode.h"
31 #include "FormDataList.h"
33 #include "HTMLDocument.h"
34 #include "HTMLFormElement.h"
35 #include "HTMLImageLoader.h"
36 #include "HTMLMetaElement.h"
37 #include "HTMLNames.h"
38 #include "HTMLParamElement.h"
39 #include "HTMLParserIdioms.h"
40 #include "MIMETypeRegistry.h"
43 #include "PluginViewBase.h"
44 #include "RenderEmbeddedObject.h"
45 #include "RenderImage.h"
46 #include "RenderWidget.h"
47 #include "ScriptEventListener.h"
54 using namespace HTMLNames;
56 inline HTMLObjectElement::HTMLObjectElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form, bool createdByParser)
57 : HTMLPlugInImageElement(tagName, document, createdByParser, ShouldNotPreferPlugInsForImages)
58 , FormAssociatedElement(form)
59 , m_docNamedItem(true)
60 , m_useFallbackContent(false)
62 ASSERT(hasTagName(objectTag));
64 setForm(findFormAncestor());
66 this->form()->registerFormElement(this);
69 inline HTMLObjectElement::~HTMLObjectElement()
72 form()->removeFormElement(this);
75 PassRefPtr<HTMLObjectElement> HTMLObjectElement::create(const QualifiedName& tagName, Document* document, HTMLFormElement* form, bool createdByParser)
77 return adoptRef(new HTMLObjectElement(tagName, document, form, createdByParser));
80 RenderWidget* HTMLObjectElement::renderWidgetForJSBindings()
82 document()->updateLayoutIgnorePendingStylesheets();
83 return renderPart(); // This will return 0 if the renderer is not a RenderPart.
86 void HTMLObjectElement::parseMappedAttribute(Attribute* attr)
88 if (attr->name() == formAttr)
89 formAttributeChanged();
90 else if (attr->name() == typeAttr) {
91 m_serviceType = attr->value().lower();
92 size_t pos = m_serviceType.find(";");
94 m_serviceType = m_serviceType.left(pos);
96 setNeedsWidgetUpdate(true);
97 if (!isImageType() && m_imageLoader)
98 m_imageLoader.clear();
99 } else if (attr->name() == dataAttr) {
100 m_url = stripLeadingAndTrailingHTMLSpaces(attr->value());
102 setNeedsWidgetUpdate(true);
105 m_imageLoader = adoptPtr(new HTMLImageLoader(this));
106 m_imageLoader->updateFromElementIgnoringPreviousError();
109 } else if (attr->name() == classidAttr) {
110 m_classId = attr->value();
112 setNeedsWidgetUpdate(true);
113 } else if (attr->name() == onloadAttr)
114 setAttributeEventListener(eventNames().loadEvent, createAttributeEventListener(this, attr));
115 else if (attr->name() == onbeforeloadAttr)
116 setAttributeEventListener(eventNames().beforeloadEvent, createAttributeEventListener(this, attr));
117 else if (attr->name() == nameAttr) {
118 const AtomicString& newName = attr->value();
119 if (isDocNamedItem() && inDocument() && document()->isHTMLDocument()) {
120 HTMLDocument* document = static_cast<HTMLDocument*>(this->document());
121 document->removeNamedItem(m_name);
122 document->addNamedItem(newName);
125 } else if (attr->name() == borderAttr)
126 applyBorderAttribute(attr);
127 else if (isIdAttributeName(attr->name())) {
128 const AtomicString& newId = attr->value();
129 if (isDocNamedItem() && inDocument() && document()->isHTMLDocument()) {
130 HTMLDocument* document = static_cast<HTMLDocument*>(this->document());
131 document->removeExtraNamedItem(m_id);
132 document->addExtraNamedItem(newId);
135 // also call superclass
136 HTMLPlugInImageElement::parseMappedAttribute(attr);
138 HTMLPlugInImageElement::parseMappedAttribute(attr);
141 static void mapDataParamToSrc(Vector<String>* paramNames, Vector<String>* paramValues)
143 // Some plugins don't understand the "data" attribute of the OBJECT tag (i.e. Real and WMP
144 // require "src" attribute).
145 int srcIndex = -1, dataIndex = -1;
146 for (unsigned int i = 0; i < paramNames->size(); ++i) {
147 if (equalIgnoringCase((*paramNames)[i], "src"))
149 else if (equalIgnoringCase((*paramNames)[i], "data"))
153 if (srcIndex == -1 && dataIndex != -1) {
154 paramNames->append("src");
155 paramValues->append((*paramValues)[dataIndex]);
159 // FIXME: This function should not deal with url or serviceType!
160 void HTMLObjectElement::parametersForPlugin(Vector<String>& paramNames, Vector<String>& paramValues, String& url, String& serviceType)
162 HashSet<StringImpl*, CaseFoldingHash> uniqueParamNames;
165 // Scan the PARAM children and store their name/value pairs.
166 // Get the URL and type from the params if we don't already have them.
167 for (Node* child = firstChild(); child; child = child->nextSibling()) {
168 if (!child->hasTagName(paramTag))
171 HTMLParamElement* p = static_cast<HTMLParamElement*>(child);
172 String name = p->name();
176 uniqueParamNames.add(name.impl());
177 paramNames.append(p->name());
178 paramValues.append(p->value());
180 // FIXME: url adjustment does not belong in this function.
181 if (url.isEmpty() && urlParameter.isEmpty() && (equalIgnoringCase(name, "src") || equalIgnoringCase(name, "movie") || equalIgnoringCase(name, "code") || equalIgnoringCase(name, "url")))
182 urlParameter = stripLeadingAndTrailingHTMLSpaces(p->value());
183 // FIXME: serviceType calculation does not belong in this function.
184 if (serviceType.isEmpty() && equalIgnoringCase(name, "type")) {
185 serviceType = p->value();
186 size_t pos = serviceType.find(";");
188 serviceType = serviceType.left(pos);
192 // When OBJECT is used for an applet via Sun's Java plugin, the CODEBASE attribute in the tag
193 // points to the Java plugin itself (an ActiveX component) while the actual applet CODEBASE is
194 // in a PARAM tag. See <http://java.sun.com/products/plugin/1.2/docs/tags.html>. This means
195 // we have to explicitly suppress the tag's CODEBASE attribute if there is none in a PARAM,
196 // else our Java plugin will misinterpret it. [4004531]
198 if (MIMETypeRegistry::isJavaAppletMIMEType(serviceType)) {
199 codebase = "codebase";
200 uniqueParamNames.add(codebase.impl()); // pretend we found it in a PARAM already
203 // Turn the attributes of the <object> element into arrays, but don't override <param> values.
204 NamedNodeMap* attributes = this->attributes(true);
206 for (unsigned i = 0; i < attributes->length(); ++i) {
207 Attribute* it = attributes->attributeItem(i);
208 const AtomicString& name = it->name().localName();
209 if (!uniqueParamNames.contains(name.impl())) {
210 paramNames.append(name.string());
211 paramValues.append(it->value().string());
216 mapDataParamToSrc(¶mNames, ¶mValues);
218 // HTML5 says that an object resource's URL is specified by the object's data
219 // attribute, not by a param element. However, for compatibility, allow the
220 // resource's URL to be given by a param named "src", "movie", "code" or "url"
221 // if we know that resource points to a plug-in.
222 if (url.isEmpty() && !urlParameter.isEmpty()) {
223 SubframeLoader* loader = document()->frame()->loader()->subframeLoader();
224 #if ENABLE(TIZEN_PLUGIN_LOADING_FLOW)
225 if (loader->resourceWillUsePlugin(urlParameter, serviceType, shouldPreferPlugInsForImages(), true))
227 if (loader->resourceWillUsePlugin(urlParameter, serviceType, shouldPreferPlugInsForImages()))
234 bool HTMLObjectElement::hasFallbackContent() const
236 for (Node* child = firstChild(); child; child = child->nextSibling()) {
237 // Ignore whitespace-only text, and <param> tags, any other content is fallback content.
238 if (child->isTextNode()) {
239 if (!static_cast<Text*>(child)->containsOnlyWhitespace())
241 } else if (!child->hasTagName(paramTag))
247 bool HTMLObjectElement::shouldAllowQuickTimeClassIdQuirk()
249 // This site-specific hack maintains compatibility with Mac OS X Wiki Server,
250 // which embeds QuickTime movies using an object tag containing QuickTime's
251 // ActiveX classid. Treat this classid as valid only if OS X Server's unique
252 // 'generator' meta tag is present. Only apply this quirk if there is no
253 // fallback content, which ensures the quirk will disable itself if Wiki
254 // Server is updated to generate an alternate embed tag as fallback content.
255 if (!document()->page()
256 || !document()->page()->settings()->needsSiteSpecificQuirks()
257 || hasFallbackContent()
258 || !equalIgnoringCase(classId(), "clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B"))
261 RefPtr<NodeList> metaElements = document()->getElementsByTagName(HTMLNames::metaTag.localName());
262 unsigned length = metaElements->length();
263 for (unsigned i = 0; i < length; ++i) {
264 ASSERT(metaElements->item(i)->isHTMLElement());
265 HTMLMetaElement* metaElement = static_cast<HTMLMetaElement*>(metaElements->item(i));
266 if (equalIgnoringCase(metaElement->name(), "generator") && metaElement->content().startsWith("Mac OS X Server Web Services Server", false))
273 bool HTMLObjectElement::hasValidClassId()
276 if (equalIgnoringCase(serviceType(), "application/x-qt-plugin") || equalIgnoringCase(serviceType(), "application/x-qt-styled-widget"))
280 if (MIMETypeRegistry::isJavaAppletMIMEType(serviceType()) && classId().startsWith("java:", false))
283 if (shouldAllowQuickTimeClassIdQuirk())
286 // HTML5 says that fallback content should be rendered if a non-empty
287 // classid is specified for which the UA can't find a suitable plug-in.
288 return classId().isEmpty();
291 // FIXME: This should be unified with HTMLEmbedElement::updateWidget and
292 // moved down into HTMLPluginImageElement.cpp
293 void HTMLObjectElement::updateWidget(PluginCreationOption pluginCreationOption)
295 ASSERT(!renderEmbeddedObject()->pluginCrashedOrWasMissing());
296 // FIXME: We should ASSERT(needsWidgetUpdate()), but currently
297 // FrameView::updateWidget() calls updateWidget(false) without checking if
298 // the widget actually needs updating!
299 setNeedsWidgetUpdate(false);
300 // FIXME: This should ASSERT isFinishedParsingChildren() instead.
301 if (!isFinishedParsingChildren())
304 String url = this->url();
305 String serviceType = this->serviceType();
307 // FIXME: These should be joined into a PluginParameters class.
308 Vector<String> paramNames;
309 Vector<String> paramValues;
310 parametersForPlugin(paramNames, paramValues, url, serviceType);
312 // Note: url is modified above by parametersForPlugin.
313 if (!allowedToLoadFrameURL(url))
316 bool fallbackContent = hasFallbackContent();
317 renderEmbeddedObject()->setHasFallbackContent(fallbackContent);
319 if (pluginCreationOption == CreateOnlyNonNetscapePlugins && wouldLoadAsNetscapePlugin(url, serviceType))
322 ASSERT(!m_inBeforeLoadEventHandler);
323 m_inBeforeLoadEventHandler = true;
324 bool beforeLoadAllowedLoad = dispatchBeforeLoadEvent(url);
325 m_inBeforeLoadEventHandler = false;
327 // beforeload events can modify the DOM, potentially causing
328 // RenderWidget::destroy() to be called. Ensure we haven't been
329 // destroyed before continuing.
330 // FIXME: Should this render fallback content?
334 RefPtr<HTMLObjectElement> protect(this); // Loading the plugin might remove us from the document.
335 SubframeLoader* loader = document()->frame()->loader()->subframeLoader();
336 bool success = beforeLoadAllowedLoad && hasValidClassId() && loader->requestObject(this, url, getAttribute(nameAttr), serviceType, paramNames, paramValues);
338 if (!success && fallbackContent)
339 renderFallbackContent();
342 bool HTMLObjectElement::rendererIsNeeded(const NodeRenderingContext& context)
344 // FIXME: This check should not be needed, detached documents never render!
345 Frame* frame = document()->frame();
349 return HTMLPlugInImageElement::rendererIsNeeded(context);
352 void HTMLObjectElement::insertedIntoDocument()
354 HTMLPlugInImageElement::insertedIntoDocument();
358 if (isDocNamedItem() && document()->isHTMLDocument()) {
359 HTMLDocument* document = static_cast<HTMLDocument*>(this->document());
360 document->addNamedItem(m_name);
361 document->addExtraNamedItem(m_id);
364 FormAssociatedElement::insertedIntoDocument();
367 void HTMLObjectElement::removedFromDocument()
369 if (isDocNamedItem() && document()->isHTMLDocument()) {
370 HTMLDocument* document = static_cast<HTMLDocument*>(this->document());
371 document->removeNamedItem(m_name);
372 document->removeExtraNamedItem(m_id);
375 HTMLPlugInImageElement::removedFromDocument();
376 FormAssociatedElement::removedFromDocument();
379 void HTMLObjectElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
381 updateDocNamedItem();
382 if (inDocument() && !useFallbackContent()) {
383 setNeedsWidgetUpdate(true);
384 setNeedsStyleRecalc();
386 HTMLPlugInImageElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
389 bool HTMLObjectElement::isURLAttribute(Attribute *attr) const
391 return attr->name() == dataAttr || (attr->name() == usemapAttr && attr->value().string()[0] != '#') || HTMLPlugInImageElement::isURLAttribute(attr);
394 const QualifiedName& HTMLObjectElement::imageSourceAttributeName() const
399 void HTMLObjectElement::renderFallbackContent()
401 if (useFallbackContent())
407 // Before we give up and use fallback content, check to see if this is a MIME type issue.
408 if (m_imageLoader && m_imageLoader->image() && m_imageLoader->image()->status() != CachedResource::LoadError) {
409 m_serviceType = m_imageLoader->image()->response().mimeType();
410 if (!isImageType()) {
411 // If we don't think we have an image type anymore, then clear the image from the loader.
412 m_imageLoader->setImage(0);
418 m_useFallbackContent = true;
420 // FIXME: Style gets recalculated which is suboptimal.
425 // FIXME: This should be removed, all callers are almost certainly wrong.
426 static bool isRecognizedTagName(const QualifiedName& tagName)
428 DEFINE_STATIC_LOCAL(HashSet<AtomicStringImpl*>, tagList, ());
429 if (tagList.isEmpty()) {
431 QualifiedName** tags = HTMLNames::getHTMLTags(&tagCount);
432 for (size_t i = 0; i < tagCount; i++) {
433 if (*tags[i] == bgsoundTag
434 || *tags[i] == commandTag
435 || *tags[i] == detailsTag
436 || *tags[i] == figcaptionTag
437 || *tags[i] == figureTag
438 || *tags[i] == summaryTag
439 || *tags[i] == trackTag) {
440 // Even though we have atoms for these tags, we don't want to
441 // treat them as "recognized tags" for the purpose of parsing
442 // because that changes how we parse documents.
445 tagList.add(tags[i]->localName().impl());
448 return tagList.contains(tagName.localName().impl());
451 void HTMLObjectElement::updateDocNamedItem()
453 // The rule is "<object> elements with no children other than
454 // <param> elements, unknown elements and whitespace can be
455 // found by name in a document, and other <object> elements cannot."
456 bool wasNamedItem = m_docNamedItem;
457 bool isNamedItem = true;
458 Node* child = firstChild();
459 while (child && isNamedItem) {
460 if (child->isElementNode()) {
461 Element* element = static_cast<Element*>(child);
462 // FIXME: Use of isRecognizedTagName is almost certainly wrong here.
463 if (isRecognizedTagName(element->tagQName()) && !element->hasTagName(paramTag))
465 } else if (child->isTextNode()) {
466 if (!static_cast<Text*>(child)->containsOnlyWhitespace())
470 child = child->nextSibling();
472 if (isNamedItem != wasNamedItem && document()->isHTMLDocument()) {
473 HTMLDocument* document = static_cast<HTMLDocument*>(this->document());
475 document->addNamedItem(m_name);
476 document->addExtraNamedItem(m_id);
478 document->removeNamedItem(m_name);
479 document->removeExtraNamedItem(m_id);
482 m_docNamedItem = isNamedItem;
485 bool HTMLObjectElement::containsJavaApplet() const
487 if (MIMETypeRegistry::isJavaAppletMIMEType(getAttribute(typeAttr)))
490 for (Element* child = firstElementChild(); child; child = child->nextElementSibling()) {
491 if (child->hasTagName(paramTag)
492 && equalIgnoringCase(child->getAttribute(nameAttr), "type")
493 && MIMETypeRegistry::isJavaAppletMIMEType(child->getAttribute(valueAttr).string()))
495 if (child->hasTagName(objectTag)
496 && static_cast<HTMLObjectElement*>(child)->containsJavaApplet())
498 if (child->hasTagName(appletTag))
505 void HTMLObjectElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const
507 HTMLPlugInImageElement::addSubresourceAttributeURLs(urls);
509 addSubresourceURL(urls, document()->completeURL(getAttribute(dataAttr)));
511 // FIXME: Passing a string that starts with "#" to the completeURL function does
512 // not seem like it would work. The image element has similar but not identical code.
513 const AtomicString& useMap = getAttribute(usemapAttr);
514 if (useMap.startsWith("#"))
515 addSubresourceURL(urls, document()->completeURL(useMap));
518 void HTMLObjectElement::willMoveToNewOwnerDocument()
520 FormAssociatedElement::willMoveToNewOwnerDocument();
521 HTMLPlugInImageElement::willMoveToNewOwnerDocument();
524 void HTMLObjectElement::insertedIntoTree(bool deep)
526 FormAssociatedElement::insertedIntoTree();
527 HTMLPlugInImageElement::insertedIntoTree(deep);
530 void HTMLObjectElement::removedFromTree(bool deep)
532 FormAssociatedElement::removedFromTree();
533 HTMLPlugInImageElement::removedFromTree(deep);
536 bool HTMLObjectElement::appendFormData(FormDataList& encoding, bool)
538 if (name().isEmpty())
541 Widget* widget = pluginWidget();
542 if (!widget || !widget->isPluginViewBase())
545 if (!static_cast<PluginViewBase*>(widget)->getFormValue(value))
547 encoding.appendData(name(), value);
551 const AtomicString& HTMLObjectElement::formControlName() const
553 return m_name.isNull() ? emptyAtom : m_name;
556 HTMLFormElement* HTMLObjectElement::virtualForm() const
558 return FormAssociatedElement::form();
561 #if ENABLE(MICRODATA)
562 String HTMLObjectElement::itemValueText() const
564 return getURLAttribute(dataAttr);
567 void HTMLObjectElement::setItemValueText(const String& value, ExceptionCode& ec)
569 setAttribute(dataAttr, value, ec);