Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / core / html / HTMLObjectElement.cpp
blob216f06af5a6c9a42fed2d88179fb244201d77131
1 /*
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.
24 #include "config.h"
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"
47 namespace blink {
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()
60 #if !ENABLE(OILPAN)
61 setForm(0);
62 #endif
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)
86 return true;
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);
94 else
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);
110 if (!layoutObject())
111 requestPluginCreationWithoutLayoutObjectIfPossible();
112 } else if (name == dataAttr) {
113 m_url = stripLeadingAndTrailingHTMLSpaces(value);
114 if (layoutObject() && 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);
122 } else if (name == classidAttr) {
123 m_classId = value;
124 reloadPluginOnAttributeChange(name);
125 } else {
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"))
137 srcIndex = i;
138 else if (equalIgnoringCase((*paramNames)[i], "data"))
139 dataIndex = i;
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;
152 String urlParameter;
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;
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]
182 String codebase;
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(&paramNames, &paramValues);
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);
206 bool useFallback;
207 if (shouldUsePlugin(completedURL, serviceType, false, useFallback))
208 url = urlParameter;
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())
219 return true;
220 } else if (!isHTMLParamElement(*child)) {
221 return true;
224 return false;
227 bool HTMLObjectElement::hasValidClassId()
229 if (MIMETypeRegistry::isJavaAppletMIMEType(m_serviceType) && classId().startsWith("java:", TextCaseInsensitive))
230 return true;
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)
239 // Following,
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;
252 } else {
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();
271 return;
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
276 // security bugs.
277 if (!SubframeLoadingDisabler::canLoadFrame(*this)) {
278 dispatchErrorEvent();
279 return;
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();
293 return;
296 // FIXME: Is it possible to get here without a layoutObject now that we don't have beforeload events?
297 if (!layoutObject())
298 return;
300 if (!hasValidClassId() || !requestObject(url, serviceType, paramNames, paramValues)) {
301 if (!url.isEmpty())
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
344 return dataAttr;
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())
358 reattach();
359 else
360 lazyReattachIfAttached();
363 void HTMLObjectElement::renderFallbackContent()
365 if (useFallbackContent())
366 return;
368 if (!inDocument())
369 return;
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();
378 return;
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())
393 return false;
395 for (HTMLElement& element : Traversal<HTMLElement>::descendantsOf(*this)) {
396 if (isHTMLObjectElement(element) || isHTMLEmbedElement(element))
397 return false;
399 return true;
402 bool HTMLObjectElement::containsJavaApplet() const
404 if (MIMETypeRegistry::isJavaAppletMIMEType(getAttribute(typeAttr)))
405 return true;
407 for (HTMLElement& child : Traversal<HTMLElement>::childrenOf(*this)) {
408 if (isHTMLParamElement(child)
409 && equalIgnoringCase(child.getNameAttribute(), "type")
410 && MIMETypeRegistry::isJavaAppletMIMEType(child.getAttribute(valueAttr).string()))
411 return true;
412 if (isHTMLObjectElement(child) && toHTMLObjectElement(child).containsJavaApplet())
413 return true;
416 return false;
419 void HTMLObjectElement::didMoveToNewDocument(Document& oldDocument)
421 FormAssociatedElement::didMoveToNewDocument(oldDocument);
422 HTMLPlugInElement::didMoveToNewDocument(oldDocument);
425 void HTMLObjectElement::appendToFormData(FormData& formData)
427 if (name().isEmpty())
428 return;
430 Widget* widget = pluginWidget();
431 if (!widget || !widget->isPluginView())
432 return;
433 String value;
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;