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 "core/html/HTMLObjectElement.h"
27 #include "HTMLNames.h"
28 #include "bindings/v8/ScriptEventListener.h"
29 #include "core/dom/Attribute.h"
30 #include "core/dom/ElementTraversal.h"
31 #include "core/dom/Text.h"
32 #include "core/dom/shadow/ShadowRoot.h"
33 #include "core/fetch/ImageResource.h"
34 #include "core/html/FormDataList.h"
35 #include "core/html/HTMLCollection.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"
49 using namespace HTMLNames;
51 inline HTMLObjectElement::HTMLObjectElement(Document& document, HTMLFormElement* form, bool createdByParser)
52 : HTMLPlugInElement(objectTag, document, createdByParser, ShouldNotPreferPlugInsForImages)
53 , m_useFallbackContent(false)
55 ScriptWrappable::init(this);
56 associateByParser(form);
59 inline HTMLObjectElement::~HTMLObjectElement()
66 PassRefPtrWillBeRawPtr<HTMLObjectElement> HTMLObjectElement::create(Document& document, HTMLFormElement* form, bool createdByParser)
68 RefPtrWillBeRawPtr<HTMLObjectElement> element = adoptRefWillBeRefCountedGarbageCollected(new HTMLObjectElement(document, form, createdByParser));
69 element->ensureUserAgentShadowRoot();
70 return element.release();
73 void HTMLObjectElement::trace(Visitor* visitor)
75 FormAssociatedElement::trace(visitor);
76 HTMLPlugInElement::trace(visitor);
79 RenderWidget* HTMLObjectElement::existingRenderWidget() const
81 return renderPart(); // This will return 0 if the renderer is not a RenderPart.
84 bool HTMLObjectElement::isPresentationAttribute(const QualifiedName& name) const
86 if (name == borderAttr)
88 return HTMLPlugInElement::isPresentationAttribute(name);
91 void HTMLObjectElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style)
93 if (name == borderAttr)
94 applyBorderAttributeToStyle(value, style);
96 HTMLPlugInElement::collectStyleForPresentationAttribute(name, value, style);
99 void HTMLObjectElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
101 if (name == formAttr)
102 formAttributeChanged();
103 else if (name == typeAttr) {
104 m_serviceType = value.lower();
105 size_t pos = m_serviceType.find(";");
106 if (pos != kNotFound)
107 m_serviceType = m_serviceType.left(pos);
108 // FIXME: What is the right thing to do here? Should we supress the
109 // reload stuff when a persistable widget-type is specified?
110 reloadPluginOnAttributeChange(name);
112 requestPluginCreationWithoutRendererIfPossible();
113 } else if (name == dataAttr) {
114 m_url = stripLeadingAndTrailingHTMLSpaces(value);
115 if (renderer() && isImageType()) {
116 setNeedsWidgetUpdate(true);
118 m_imageLoader = adoptPtr(new HTMLImageLoader(this));
119 m_imageLoader->updateFromElementIgnoringPreviousError();
121 reloadPluginOnAttributeChange(name);
123 } else if (name == classidAttr) {
125 reloadPluginOnAttributeChange(name);
127 HTMLPlugInElement::parseAttribute(name, value);
131 static void mapDataParamToSrc(Vector<String>* paramNames, Vector<String>* paramValues)
133 // Some plugins don't understand the "data" attribute of the OBJECT tag (i.e. Real and WMP
134 // require "src" attribute).
135 int srcIndex = -1, dataIndex = -1;
136 for (unsigned i = 0; i < paramNames->size(); ++i) {
137 if (equalIgnoringCase((*paramNames)[i], "src"))
139 else if (equalIgnoringCase((*paramNames)[i], "data"))
143 if (srcIndex == -1 && dataIndex != -1) {
144 paramNames->append("src");
145 paramValues->append((*paramValues)[dataIndex]);
149 // FIXME: This function should not deal with url or serviceType!
150 void HTMLObjectElement::parametersForPlugin(Vector<String>& paramNames, Vector<String>& paramValues, String& url, String& serviceType)
152 HashSet<StringImpl*, CaseFoldingHash> uniqueParamNames;
155 // Scan the PARAM children and store their name/value pairs.
156 // Get the URL and type from the params if we don't already have them.
157 for (HTMLParamElement* p = Traversal<HTMLParamElement>::firstChild(*this); p; p = Traversal<HTMLParamElement>::nextSibling(*p)) {
158 String name = p->name();
162 uniqueParamNames.add(name.impl());
163 paramNames.append(p->name());
164 paramValues.append(p->value());
166 // FIXME: url adjustment does not belong in this function.
167 if (url.isEmpty() && urlParameter.isEmpty() && (equalIgnoringCase(name, "src") || equalIgnoringCase(name, "movie") || equalIgnoringCase(name, "code") || equalIgnoringCase(name, "url")))
168 urlParameter = stripLeadingAndTrailingHTMLSpaces(p->value());
169 // FIXME: serviceType calculation does not belong in this function.
170 if (serviceType.isEmpty() && equalIgnoringCase(name, "type")) {
171 serviceType = p->value();
172 size_t pos = serviceType.find(";");
173 if (pos != kNotFound)
174 serviceType = serviceType.left(pos);
178 // When OBJECT is used for an applet via Sun's Java plugin, the CODEBASE attribute in the tag
179 // points to the Java plugin itself (an ActiveX component) while the actual applet CODEBASE is
180 // in a PARAM tag. See <http://java.sun.com/products/plugin/1.2/docs/tags.html>. This means
181 // we have to explicitly suppress the tag's CODEBASE attribute if there is none in a PARAM,
182 // else our Java plugin will misinterpret it. [4004531]
184 if (MIMETypeRegistry::isJavaAppletMIMEType(serviceType)) {
185 codebase = "codebase";
186 uniqueParamNames.add(codebase.impl()); // pretend we found it in a PARAM already
189 // Turn the attributes of the <object> element into arrays, but don't override <param> values.
190 if (hasAttributes()) {
191 unsigned attributeCount = this->attributeCount();
192 for (unsigned i = 0; i < attributeCount; ++i) {
193 const Attribute& attribute = attributeItem(i);
194 const AtomicString& name = attribute.name().localName();
195 if (!uniqueParamNames.contains(name.impl())) {
196 paramNames.append(name.string());
197 paramValues.append(attribute.value().string());
202 mapDataParamToSrc(¶mNames, ¶mValues);
204 // HTML5 says that an object resource's URL is specified by the object's data
205 // attribute, not by a param element. However, for compatibility, allow the
206 // resource's URL to be given by a param named "src", "movie", "code" or "url"
207 // if we know that resource points to a plug-in.
208 if (url.isEmpty() && !urlParameter.isEmpty()) {
209 KURL completedURL = document().completeURL(urlParameter);
211 if (shouldUsePlugin(completedURL, serviceType, false, useFallback))
217 bool HTMLObjectElement::hasFallbackContent() const
219 for (Node* child = firstChild(); child; child = child->nextSibling()) {
220 // Ignore whitespace-only text, and <param> tags, any other content is fallback content.
221 if (child->isTextNode()) {
222 if (!toText(child)->containsOnlyWhitespace())
224 } else if (!isHTMLParamElement(*child)) {
231 bool HTMLObjectElement::shouldAllowQuickTimeClassIdQuirk()
233 // This site-specific hack maintains compatibility with Mac OS X Wiki Server,
234 // which embeds QuickTime movies using an object tag containing QuickTime's
235 // ActiveX classid. Treat this classid as valid only if OS X Server's unique
236 // 'generator' meta tag is present. Only apply this quirk if there is no
237 // fallback content, which ensures the quirk will disable itself if Wiki
238 // Server is updated to generate an alternate embed tag as fallback content.
239 if (!document().settings()
240 || !document().settings()->needsSiteSpecificQuirks()
241 || hasFallbackContent()
242 || !equalIgnoringCase(classId(), "clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B"))
245 RefPtr<HTMLCollection> metaElements = document().getElementsByTagName(HTMLNames::metaTag.localName());
246 unsigned length = metaElements->length();
247 for (unsigned i = 0; i < length; ++i) {
248 ASSERT(metaElements->item(i)->isHTMLElement());
249 HTMLMetaElement* metaElement = toHTMLMetaElement(metaElements->item(i));
250 if (equalIgnoringCase(metaElement->name(), "generator") && metaElement->content().startsWith("Mac OS X Server Web Services Server", false))
257 bool HTMLObjectElement::hasValidClassId()
259 if (MIMETypeRegistry::isJavaAppletMIMEType(m_serviceType) && classId().startsWith("java:", false))
262 if (shouldAllowQuickTimeClassIdQuirk())
265 // HTML5 says that fallback content should be rendered if a non-empty
266 // classid is specified for which the UA can't find a suitable plug-in.
267 return classId().isEmpty();
270 void HTMLObjectElement::reloadPluginOnAttributeChange(const QualifiedName& name)
273 // http://www.whatwg.org/specs/web-apps/current-work/#the-object-element
274 // (Enumerated list below "Whenever one of the following conditions occur:")
276 // the updating of certain attributes should bring about "redetermination"
277 // of what the element contains.
278 bool needsInvalidation;
279 if (name == typeAttr) {
280 needsInvalidation = !fastHasAttribute(classidAttr) && !fastHasAttribute(dataAttr);
281 } else if (name == dataAttr) {
282 needsInvalidation = !fastHasAttribute(classidAttr);
283 } else if (name == classidAttr) {
284 needsInvalidation = true;
286 ASSERT_NOT_REACHED();
287 needsInvalidation = false;
289 setNeedsWidgetUpdate(true);
290 if (needsInvalidation)
291 setNeedsStyleRecalc(SubtreeStyleChange);
294 // FIXME: This should be unified with HTMLEmbedElement::updateWidget and
295 // moved down into HTMLPluginElement.cpp
296 void HTMLObjectElement::updateWidgetInternal()
298 ASSERT(!renderEmbeddedObject()->showsUnavailablePluginIndicator());
299 ASSERT(needsWidgetUpdate());
300 setNeedsWidgetUpdate(false);
301 // FIXME: This should ASSERT isFinishedParsingChildren() instead.
302 if (!isFinishedParsingChildren()) {
303 dispatchErrorEvent();
307 // FIXME: I'm not sure it's ever possible to get into updateWidget during a
308 // removal, but just in case we should avoid loading the frame to prevent
310 if (!SubframeLoadingDisabler::canLoadFrame(*this)) {
311 dispatchErrorEvent();
315 String url = this->url();
316 String serviceType = m_serviceType;
318 // FIXME: These should be joined into a PluginParameters class.
319 Vector<String> paramNames;
320 Vector<String> paramValues;
321 parametersForPlugin(paramNames, paramValues, url, serviceType);
323 // Note: url is modified above by parametersForPlugin.
324 if (!allowedToLoadFrameURL(url)) {
325 dispatchErrorEvent();
329 bool fallbackContent = hasFallbackContent();
330 renderEmbeddedObject()->setHasFallbackContent(fallbackContent);
332 // FIXME: Is it possible to get here without a renderer now that we don't have beforeload events?
336 if (!hasValidClassId() || !requestObject(url, serviceType, paramNames, paramValues)) {
338 dispatchErrorEvent();
340 renderFallbackContent();
344 bool HTMLObjectElement::rendererIsNeeded(const RenderStyle& style)
346 // FIXME: This check should not be needed, detached documents never render!
347 if (!document().frame())
349 return HTMLPlugInElement::rendererIsNeeded(style);
352 Node::InsertionNotificationRequest HTMLObjectElement::insertedInto(ContainerNode* insertionPoint)
354 HTMLPlugInElement::insertedInto(insertionPoint);
355 FormAssociatedElement::insertedInto(insertionPoint);
356 return InsertionDone;
359 void HTMLObjectElement::removedFrom(ContainerNode* insertionPoint)
361 HTMLPlugInElement::removedFrom(insertionPoint);
362 FormAssociatedElement::removedFrom(insertionPoint);
365 void HTMLObjectElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
367 if (inDocument() && !useFallbackContent()) {
368 setNeedsWidgetUpdate(true);
369 setNeedsStyleRecalc(SubtreeStyleChange);
371 HTMLPlugInElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
374 bool HTMLObjectElement::isURLAttribute(const Attribute& attribute) const
376 return attribute.name() == codebaseAttr || attribute.name() == dataAttr
377 || (attribute.name() == usemapAttr && attribute.value().string()[0] != '#')
378 || HTMLPlugInElement::isURLAttribute(attribute);
381 bool HTMLObjectElement::hasLegalLinkAttribute(const QualifiedName& name) const
383 return name == classidAttr || name == dataAttr || name == codebaseAttr || HTMLPlugInElement::hasLegalLinkAttribute(name);
386 const QualifiedName& HTMLObjectElement::subResourceAttributeName() const
391 const AtomicString HTMLObjectElement::imageSourceURL() const
393 return getAttribute(dataAttr);
396 // FIXME: Remove this hack.
397 void HTMLObjectElement::reattachFallbackContent()
399 // This can happen inside of attach() in the middle of a recalcStyle so we need to
400 // reattach synchronously here.
401 if (document().inStyleRecalc())
404 lazyReattachIfAttached();
407 void HTMLObjectElement::renderFallbackContent()
409 if (useFallbackContent())
415 // Before we give up and use fallback content, check to see if this is a MIME type issue.
416 if (m_imageLoader && m_imageLoader->image() && m_imageLoader->image()->status() != Resource::LoadError) {
417 m_serviceType = m_imageLoader->image()->response().mimeType();
418 if (!isImageType()) {
419 // If we don't think we have an image type anymore, then clear the image from the loader.
420 m_imageLoader->setImage(0);
421 reattachFallbackContent();
426 m_useFallbackContent = true;
428 // FIXME: Style gets recalculated which is suboptimal.
429 reattachFallbackContent();
432 bool HTMLObjectElement::isExposed() const
434 // http://www.whatwg.org/specs/web-apps/current-work/#exposed
435 for (HTMLObjectElement* ancestor = Traversal<HTMLObjectElement>::firstAncestor(*this); ancestor; ancestor = Traversal<HTMLObjectElement>::firstAncestor(*ancestor)) {
436 if (ancestor->isExposed())
439 for (HTMLElement* element = Traversal<HTMLElement>::firstWithin(*this); element; element = Traversal<HTMLElement>::next(*element, this)) {
440 if (isHTMLObjectElement(*element) || isHTMLEmbedElement(*element))
446 bool HTMLObjectElement::containsJavaApplet() const
448 if (MIMETypeRegistry::isJavaAppletMIMEType(getAttribute(typeAttr)))
451 for (HTMLElement* child = Traversal<HTMLElement>::firstChild(*this); child; child = Traversal<HTMLElement>::nextSibling(*child)) {
452 if (isHTMLParamElement(*child)
453 && equalIgnoringCase(child->getNameAttribute(), "type")
454 && MIMETypeRegistry::isJavaAppletMIMEType(child->getAttribute(valueAttr).string()))
456 if (isHTMLObjectElement(*child) && toHTMLObjectElement(*child).containsJavaApplet())
458 if (isHTMLAppletElement(*child))
465 void HTMLObjectElement::didMoveToNewDocument(Document& oldDocument)
467 FormAssociatedElement::didMoveToNewDocument(oldDocument);
468 HTMLPlugInElement::didMoveToNewDocument(oldDocument);
471 bool HTMLObjectElement::appendFormData(FormDataList& encoding, bool)
473 if (name().isEmpty())
476 Widget* widget = pluginWidget();
477 if (!widget || !widget->isPluginView())
480 if (!toPluginView(widget)->getFormValue(value))
482 encoding.appendData(name(), value);
486 HTMLFormElement* HTMLObjectElement::formOwner() const
488 return FormAssociatedElement::form();
491 bool HTMLObjectElement::isInteractiveContent() const
493 return fastHasAttribute(usemapAttr);
496 bool HTMLObjectElement::useFallbackContent() const
498 return HTMLPlugInElement::useFallbackContent() || m_useFallbackContent;