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 "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/frame/Settings.h"
36 #include "core/html/FormData.h"
37 #include "core/html/HTMLDocument.h"
38 #include "core/html/HTMLImageLoader.h"
39 #include "core/html/HTMLMetaElement.h"
40 #include "core/html/HTMLParamElement.h"
41 #include "core/html/parser/HTMLParserIdioms.h"
42 #include "core/layout/LayoutEmbeddedObject.h"
43 #include "core/plugins/PluginView.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 associateByParser(form
);
58 inline HTMLObjectElement::~HTMLObjectElement()
65 PassRefPtrWillBeRawPtr
<HTMLObjectElement
> HTMLObjectElement::create(Document
& document
, HTMLFormElement
* form
, bool createdByParser
)
67 RefPtrWillBeRawPtr
<HTMLObjectElement
> element
= adoptRefWillBeNoop(new HTMLObjectElement(document
, form
, createdByParser
));
68 element
->ensureUserAgentShadowRoot();
69 return element
.release();
72 DEFINE_TRACE(HTMLObjectElement
)
74 FormAssociatedElement::trace(visitor
);
75 HTMLPlugInElement::trace(visitor
);
78 LayoutPart
* HTMLObjectElement::existingLayoutPart() const
80 return layoutPart(); // This will return 0 if the layoutObject is not a LayoutPart.
83 bool HTMLObjectElement::isPresentationAttribute(const QualifiedName
& name
) const
85 if (name
== borderAttr
)
87 return HTMLPlugInElement::isPresentationAttribute(name
);
90 void HTMLObjectElement::collectStyleForPresentationAttribute(const QualifiedName
& name
, const AtomicString
& value
, MutableStylePropertySet
* style
)
92 if (name
== borderAttr
)
93 applyBorderAttributeToStyle(value
, style
);
95 HTMLPlugInElement::collectStyleForPresentationAttribute(name
, value
, style
);
98 void HTMLObjectElement::parseAttribute(const QualifiedName
& name
, const AtomicString
& value
)
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
);
111 requestPluginCreationWithoutLayoutObjectIfPossible();
112 } else if (name
== dataAttr
) {
113 m_url
= stripLeadingAndTrailingHTMLSpaces(value
);
114 if (layoutObject() && isImageType()) {
115 setNeedsWidgetUpdate(true);
117 m_imageLoader
= HTMLImageLoader::create(this);
118 m_imageLoader
->updateFromElement(ImageLoader::UpdateIgnorePreviousError
);
120 reloadPluginOnAttributeChange(name
);
122 } else if (name
== classidAttr
) {
124 reloadPluginOnAttributeChange(name
);
126 HTMLPlugInElement::parseAttribute(name
, value
);
130 static void mapDataParamToSrc(Vector
<String
>* paramNames
, Vector
<String
>* paramValues
)
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"))
138 else if (equalIgnoringCase((*paramNames
)[i
], "data"))
142 if (srcIndex
== -1 && dataIndex
!= -1) {
143 paramNames
->append("src");
144 paramValues
->append((*paramValues
)[dataIndex
]);
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
)
151 HashSet
<StringImpl
*, CaseFoldingHash
> uniqueParamNames
;
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();
161 uniqueParamNames
.add(name
.impl());
162 paramNames
.append(p
->name());
163 paramValues
.append(p
->value());
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
);
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]
183 if (MIMETypeRegistry::isJavaAppletMIMEType(serviceType
)) {
184 codebase
= "codebase";
185 uniqueParamNames
.add(codebase
.impl()); // pretend we found it in a PARAM already
188 // Turn the attributes of the <object> element into arrays, but don't override <param> values.
189 AttributeCollection attributes
= this->attributes();
190 for (const Attribute
& attribute
: attributes
) {
191 const AtomicString
& name
= attribute
.name().localName();
192 if (!uniqueParamNames
.contains(name
.impl())) {
193 paramNames
.append(name
.string());
194 paramValues
.append(attribute
.value().string());
198 mapDataParamToSrc(¶mNames
, ¶mValues
);
200 // HTML5 says that an object resource's URL is specified by the object's data
201 // attribute, not by a param element. However, for compatibility, allow the
202 // resource's URL to be given by a param named "src", "movie", "code" or "url"
203 // if we know that resource points to a plugin.
204 if (url
.isEmpty() && !urlParameter
.isEmpty()) {
205 KURL completedURL
= document().completeURL(urlParameter
);
207 if (shouldUsePlugin(completedURL
, serviceType
, false, useFallback
))
213 bool HTMLObjectElement::hasFallbackContent() const
215 for (Node
* child
= firstChild(); child
; child
= child
->nextSibling()) {
216 // Ignore whitespace-only text, and <param> tags, any other content is fallback content.
217 if (child
->isTextNode()) {
218 if (!toText(child
)->containsOnlyWhitespace())
220 } else if (!isHTMLParamElement(*child
)) {
227 bool HTMLObjectElement::hasValidClassId()
229 if (MIMETypeRegistry::isJavaAppletMIMEType(m_serviceType
) && classId().startsWith("java:", TextCaseInsensitive
))
232 // HTML5 says that fallback content should be rendered if a non-empty
233 // classid is specified for which the UA can't find a suitable plugin.
234 return classId().isEmpty();
237 void HTMLObjectElement::reloadPluginOnAttributeChange(const QualifiedName
& name
)
240 // http://www.whatwg.org/specs/web-apps/current-work/#the-object-element
241 // (Enumerated list below "Whenever one of the following conditions occur:")
243 // the updating of certain attributes should bring about "redetermination"
244 // of what the element contains.
245 bool needsInvalidation
;
246 if (name
== typeAttr
) {
247 needsInvalidation
= !fastHasAttribute(classidAttr
) && !fastHasAttribute(dataAttr
);
248 } else if (name
== dataAttr
) {
249 needsInvalidation
= !fastHasAttribute(classidAttr
);
250 } else if (name
== classidAttr
) {
251 needsInvalidation
= true;
253 ASSERT_NOT_REACHED();
254 needsInvalidation
= false;
256 setNeedsWidgetUpdate(true);
257 if (needsInvalidation
)
258 lazyReattachIfNeeded();
261 // FIXME: This should be unified with HTMLEmbedElement::updateWidget and
262 // moved down into HTMLPluginElement.cpp
263 void HTMLObjectElement::updateWidgetInternal()
265 ASSERT(!layoutEmbeddedObject()->showsUnavailablePluginIndicator());
266 ASSERT(needsWidgetUpdate());
267 setNeedsWidgetUpdate(false);
268 // FIXME: This should ASSERT isFinishedParsingChildren() instead.
269 if (!isFinishedParsingChildren()) {
270 dispatchErrorEvent();
274 // FIXME: I'm not sure it's ever possible to get into updateWidget during a
275 // removal, but just in case we should avoid loading the frame to prevent
277 if (!SubframeLoadingDisabler::canLoadFrame(*this)) {
278 dispatchErrorEvent();
282 String url
= this->url();
283 String serviceType
= m_serviceType
;
285 // FIXME: These should be joined into a PluginParameters class.
286 Vector
<String
> paramNames
;
287 Vector
<String
> paramValues
;
288 parametersForPlugin(paramNames
, paramValues
, url
, serviceType
);
290 // Note: url is modified above by parametersForPlugin.
291 if (!allowedToLoadFrameURL(url
)) {
292 dispatchErrorEvent();
296 // FIXME: Is it possible to get here without a layoutObject now that we don't have beforeload events?
300 if (!hasValidClassId() || !requestObject(url
, serviceType
, paramNames
, paramValues
)) {
302 dispatchErrorEvent();
303 if (hasFallbackContent())
304 renderFallbackContent();
308 Node::InsertionNotificationRequest
HTMLObjectElement::insertedInto(ContainerNode
* insertionPoint
)
310 HTMLPlugInElement::insertedInto(insertionPoint
);
311 FormAssociatedElement::insertedInto(insertionPoint
);
312 return InsertionDone
;
315 void HTMLObjectElement::removedFrom(ContainerNode
* insertionPoint
)
317 HTMLPlugInElement::removedFrom(insertionPoint
);
318 FormAssociatedElement::removedFrom(insertionPoint
);
321 void HTMLObjectElement::childrenChanged(const ChildrenChange
& change
)
323 if (inDocument() && !useFallbackContent()) {
324 setNeedsWidgetUpdate(true);
325 lazyReattachIfNeeded();
327 HTMLPlugInElement::childrenChanged(change
);
330 bool HTMLObjectElement::isURLAttribute(const Attribute
& attribute
) const
332 return attribute
.name() == codebaseAttr
|| attribute
.name() == dataAttr
333 || (attribute
.name() == usemapAttr
&& attribute
.value()[0] != '#')
334 || HTMLPlugInElement::isURLAttribute(attribute
);
337 bool HTMLObjectElement::hasLegalLinkAttribute(const QualifiedName
& name
) const
339 return name
== classidAttr
|| name
== dataAttr
|| name
== codebaseAttr
|| HTMLPlugInElement::hasLegalLinkAttribute(name
);
342 const QualifiedName
& HTMLObjectElement::subResourceAttributeName() const
347 const AtomicString
HTMLObjectElement::imageSourceURL() const
349 return getAttribute(dataAttr
);
352 // FIXME: Remove this hack.
353 void HTMLObjectElement::reattachFallbackContent()
355 // This can happen inside of attach() in the middle of a recalcStyle so we need to
356 // reattach synchronously here.
357 if (document().inStyleRecalc())
360 lazyReattachIfAttached();
363 void HTMLObjectElement::renderFallbackContent()
365 if (useFallbackContent())
371 // Before we give up and use fallback content, check to see if this is a MIME type issue.
372 if (m_imageLoader
&& m_imageLoader
->image() && m_imageLoader
->image()->status() != Resource::LoadError
) {
373 m_serviceType
= m_imageLoader
->image()->response().mimeType();
374 if (!isImageType()) {
375 // If we don't think we have an image type anymore, then clear the image from the loader.
376 m_imageLoader
->setImage(0);
377 reattachFallbackContent();
382 m_useFallbackContent
= true;
384 // FIXME: Style gets recalculated which is suboptimal.
385 reattachFallbackContent();
388 bool HTMLObjectElement::isExposed() const
390 // http://www.whatwg.org/specs/web-apps/current-work/#exposed
391 for (HTMLObjectElement
* ancestor
= Traversal
<HTMLObjectElement
>::firstAncestor(*this); ancestor
; ancestor
= Traversal
<HTMLObjectElement
>::firstAncestor(*ancestor
)) {
392 if (ancestor
->isExposed())
395 for (HTMLElement
& element
: Traversal
<HTMLElement
>::descendantsOf(*this)) {
396 if (isHTMLObjectElement(element
) || isHTMLEmbedElement(element
))
402 bool HTMLObjectElement::containsJavaApplet() const
404 if (MIMETypeRegistry::isJavaAppletMIMEType(getAttribute(typeAttr
)))
407 for (HTMLElement
& child
: Traversal
<HTMLElement
>::childrenOf(*this)) {
408 if (isHTMLParamElement(child
)
409 && equalIgnoringCase(child
.getNameAttribute(), "type")
410 && MIMETypeRegistry::isJavaAppletMIMEType(child
.getAttribute(valueAttr
).string()))
412 if (isHTMLObjectElement(child
) && toHTMLObjectElement(child
).containsJavaApplet())
419 void HTMLObjectElement::didMoveToNewDocument(Document
& oldDocument
)
421 FormAssociatedElement::didMoveToNewDocument(oldDocument
);
422 HTMLPlugInElement::didMoveToNewDocument(oldDocument
);
425 void HTMLObjectElement::appendToFormData(FormData
& formData
)
427 if (name().isEmpty())
430 Widget
* widget
= pluginWidget();
431 if (!widget
|| !widget
->isPluginView())
434 if (toPluginView(widget
)->getFormValue(value
))
435 formData
.append(name(), value
);
438 HTMLFormElement
* HTMLObjectElement::formOwner() const
440 return FormAssociatedElement::form();
443 bool HTMLObjectElement::isInteractiveContent() const
445 return fastHasAttribute(usemapAttr
);
448 bool HTMLObjectElement::useFallbackContent() const
450 return HTMLPlugInElement::useFallbackContent() || m_useFallbackContent
;