Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / html / HTMLObjectElement.cpp
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)
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     ScriptWrappable::init(this);
56     associateByParser(form);
57 }
58
59 inline HTMLObjectElement::~HTMLObjectElement()
60 {
61 #if !ENABLE(OILPAN)
62     setForm(0);
63 #endif
64 }
65
66 PassRefPtrWillBeRawPtr<HTMLObjectElement> HTMLObjectElement::create(Document& document, HTMLFormElement* form, bool createdByParser)
67 {
68     RefPtrWillBeRawPtr<HTMLObjectElement> element = adoptRefWillBeNoop(new HTMLObjectElement(document, form, createdByParser));
69     element->ensureUserAgentShadowRoot();
70     return element.release();
71 }
72
73 void HTMLObjectElement::trace(Visitor* visitor)
74 {
75     FormAssociatedElement::trace(visitor);
76     HTMLPlugInElement::trace(visitor);
77 }
78
79 RenderWidget* HTMLObjectElement::existingRenderWidget() const
80 {
81     return renderPart(); // This will return 0 if the renderer is not a RenderPart.
82 }
83
84 bool HTMLObjectElement::isPresentationAttribute(const QualifiedName& name) const
85 {
86     if (name == borderAttr)
87         return true;
88     return HTMLPlugInElement::isPresentationAttribute(name);
89 }
90
91 void HTMLObjectElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style)
92 {
93     if (name == borderAttr)
94         applyBorderAttributeToStyle(value, style);
95     else
96         HTMLPlugInElement::collectStyleForPresentationAttribute(name, value, style);
97 }
98
99 void HTMLObjectElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
100 {
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);
111         if (!renderer())
112             requestPluginCreationWithoutRendererIfPossible();
113     } else if (name == dataAttr) {
114         m_url = stripLeadingAndTrailingHTMLSpaces(value);
115         if (renderer() && isImageType()) {
116             setNeedsWidgetUpdate(true);
117             if (!m_imageLoader)
118                 m_imageLoader = HTMLImageLoader::create(this);
119             m_imageLoader->updateFromElement(ImageLoader::UpdateIgnorePreviousError);
120         } else {
121             reloadPluginOnAttributeChange(name);
122         }
123     } else if (name == classidAttr) {
124         m_classId = value;
125         reloadPluginOnAttributeChange(name);
126     } else {
127         HTMLPlugInElement::parseAttribute(name, value);
128     }
129 }
130
131 static void mapDataParamToSrc(Vector<String>* paramNames, Vector<String>* paramValues)
132 {
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"))
138             srcIndex = i;
139         else if (equalIgnoringCase((*paramNames)[i], "data"))
140             dataIndex = i;
141     }
142
143     if (srcIndex == -1 && dataIndex != -1) {
144         paramNames->append("src");
145         paramValues->append((*paramValues)[dataIndex]);
146     }
147 }
148
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)
151 {
152     HashSet<StringImpl*, CaseFoldingHash> uniqueParamNames;
153     String urlParameter;
154
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();
159         if (name.isEmpty())
160             continue;
161
162         uniqueParamNames.add(name.impl());
163         paramNames.append(p->name());
164         paramValues.append(p->value());
165
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);
175         }
176     }
177
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]
183     String codebase;
184     if (MIMETypeRegistry::isJavaAppletMIMEType(serviceType)) {
185         codebase = "codebase";
186         uniqueParamNames.add(codebase.impl()); // pretend we found it in a PARAM already
187     }
188
189     // Turn the attributes of the <object> element into arrays, but don't override <param> values.
190     AttributeCollection attributes = this->attributes();
191     AttributeCollection::iterator end = attributes.end();
192     for (AttributeCollection::iterator it = attributes.begin(); it != end; ++it) {
193         const AtomicString& name = it->name().localName();
194         if (!uniqueParamNames.contains(name.impl())) {
195             paramNames.append(name.string());
196             paramValues.append(it->value().string());
197         }
198     }
199
200     mapDataParamToSrc(&paramNames, &paramValues);
201
202     // HTML5 says that an object resource's URL is specified by the object's data
203     // attribute, not by a param element. However, for compatibility, allow the
204     // resource's URL to be given by a param named "src", "movie", "code" or "url"
205     // if we know that resource points to a plug-in.
206     if (url.isEmpty() && !urlParameter.isEmpty()) {
207         KURL completedURL = document().completeURL(urlParameter);
208         bool useFallback;
209         if (shouldUsePlugin(completedURL, serviceType, false, useFallback))
210             url = urlParameter;
211     }
212 }
213
214
215 bool HTMLObjectElement::hasFallbackContent() const
216 {
217     for (Node* child = firstChild(); child; child = child->nextSibling()) {
218         // Ignore whitespace-only text, and <param> tags, any other content is fallback content.
219         if (child->isTextNode()) {
220             if (!toText(child)->containsOnlyWhitespace())
221                 return true;
222         } else if (!isHTMLParamElement(*child)) {
223             return true;
224         }
225     }
226     return false;
227 }
228
229 bool HTMLObjectElement::hasValidClassId()
230 {
231     if (MIMETypeRegistry::isJavaAppletMIMEType(m_serviceType) && classId().startsWith("java:", false))
232         return true;
233
234     // HTML5 says that fallback content should be rendered if a non-empty
235     // classid is specified for which the UA can't find a suitable plug-in.
236     return classId().isEmpty();
237 }
238
239 void HTMLObjectElement::reloadPluginOnAttributeChange(const QualifiedName& name)
240 {
241     // Following,
242     //   http://www.whatwg.org/specs/web-apps/current-work/#the-object-element
243     //   (Enumerated list below "Whenever one of the following conditions occur:")
244     //
245     // the updating of certain attributes should bring about "redetermination"
246     // of what the element contains.
247     bool needsInvalidation;
248     if (name == typeAttr) {
249         needsInvalidation = !fastHasAttribute(classidAttr) && !fastHasAttribute(dataAttr);
250     } else if (name == dataAttr) {
251         needsInvalidation = !fastHasAttribute(classidAttr);
252     } else if (name == classidAttr) {
253         needsInvalidation = true;
254     } else {
255         ASSERT_NOT_REACHED();
256         needsInvalidation = false;
257     }
258     setNeedsWidgetUpdate(true);
259     if (needsInvalidation)
260         setNeedsStyleRecalc(SubtreeStyleChange);
261 }
262
263 // FIXME: This should be unified with HTMLEmbedElement::updateWidget and
264 // moved down into HTMLPluginElement.cpp
265 void HTMLObjectElement::updateWidgetInternal()
266 {
267     ASSERT(!renderEmbeddedObject()->showsUnavailablePluginIndicator());
268     ASSERT(needsWidgetUpdate());
269     setNeedsWidgetUpdate(false);
270     // FIXME: This should ASSERT isFinishedParsingChildren() instead.
271     if (!isFinishedParsingChildren()) {
272         dispatchErrorEvent();
273         return;
274     }
275
276     // FIXME: I'm not sure it's ever possible to get into updateWidget during a
277     // removal, but just in case we should avoid loading the frame to prevent
278     // security bugs.
279     if (!SubframeLoadingDisabler::canLoadFrame(*this)) {
280         dispatchErrorEvent();
281         return;
282     }
283
284     String url = this->url();
285     String serviceType = m_serviceType;
286
287     // FIXME: These should be joined into a PluginParameters class.
288     Vector<String> paramNames;
289     Vector<String> paramValues;
290     parametersForPlugin(paramNames, paramValues, url, serviceType);
291
292     // Note: url is modified above by parametersForPlugin.
293     if (!allowedToLoadFrameURL(url)) {
294         dispatchErrorEvent();
295         return;
296     }
297
298     // FIXME: Is it possible to get here without a renderer now that we don't have beforeload events?
299     if (!renderer())
300         return;
301
302     if (!hasValidClassId() || !requestObject(url, serviceType, paramNames, paramValues)) {
303         if (!url.isEmpty())
304             dispatchErrorEvent();
305         if (hasFallbackContent())
306             renderFallbackContent();
307     }
308 }
309
310 bool HTMLObjectElement::rendererIsNeeded(const RenderStyle& style)
311 {
312     // FIXME: This check should not be needed, detached documents never render!
313     if (!document().frame())
314         return false;
315     return HTMLPlugInElement::rendererIsNeeded(style);
316 }
317
318 Node::InsertionNotificationRequest HTMLObjectElement::insertedInto(ContainerNode* insertionPoint)
319 {
320     HTMLPlugInElement::insertedInto(insertionPoint);
321     FormAssociatedElement::insertedInto(insertionPoint);
322     return InsertionDone;
323 }
324
325 void HTMLObjectElement::removedFrom(ContainerNode* insertionPoint)
326 {
327     HTMLPlugInElement::removedFrom(insertionPoint);
328     FormAssociatedElement::removedFrom(insertionPoint);
329 }
330
331 void HTMLObjectElement::childrenChanged(const ChildrenChange& change)
332 {
333     if (inDocument() && !useFallbackContent()) {
334         setNeedsWidgetUpdate(true);
335         setNeedsStyleRecalc(SubtreeStyleChange);
336     }
337     HTMLPlugInElement::childrenChanged(change);
338 }
339
340 bool HTMLObjectElement::isURLAttribute(const Attribute& attribute) const
341 {
342     return attribute.name() == codebaseAttr || attribute.name() == dataAttr
343         || (attribute.name() == usemapAttr && attribute.value().string()[0] != '#')
344         || HTMLPlugInElement::isURLAttribute(attribute);
345 }
346
347 bool HTMLObjectElement::hasLegalLinkAttribute(const QualifiedName& name) const
348 {
349     return name == classidAttr || name == dataAttr || name == codebaseAttr || HTMLPlugInElement::hasLegalLinkAttribute(name);
350 }
351
352 const QualifiedName& HTMLObjectElement::subResourceAttributeName() const
353 {
354     return dataAttr;
355 }
356
357 const AtomicString HTMLObjectElement::imageSourceURL() const
358 {
359     return getAttribute(dataAttr);
360 }
361
362 // FIXME: Remove this hack.
363 void HTMLObjectElement::reattachFallbackContent()
364 {
365     // This can happen inside of attach() in the middle of a recalcStyle so we need to
366     // reattach synchronously here.
367     if (document().inStyleRecalc())
368         reattach();
369     else
370         lazyReattachIfAttached();
371 }
372
373 void HTMLObjectElement::renderFallbackContent()
374 {
375     if (useFallbackContent())
376         return;
377
378     if (!inDocument())
379         return;
380
381     // Before we give up and use fallback content, check to see if this is a MIME type issue.
382     if (m_imageLoader && m_imageLoader->image() && m_imageLoader->image()->status() != Resource::LoadError) {
383         m_serviceType = m_imageLoader->image()->response().mimeType();
384         if (!isImageType()) {
385             // If we don't think we have an image type anymore, then clear the image from the loader.
386             m_imageLoader->setImage(0);
387             reattachFallbackContent();
388             return;
389         }
390     }
391
392     m_useFallbackContent = true;
393
394     // FIXME: Style gets recalculated which is suboptimal.
395     reattachFallbackContent();
396 }
397
398 bool HTMLObjectElement::isExposed() const
399 {
400     // http://www.whatwg.org/specs/web-apps/current-work/#exposed
401     for (HTMLObjectElement* ancestor = Traversal<HTMLObjectElement>::firstAncestor(*this); ancestor; ancestor = Traversal<HTMLObjectElement>::firstAncestor(*ancestor)) {
402         if (ancestor->isExposed())
403             return false;
404     }
405     for (HTMLElement* element = Traversal<HTMLElement>::firstWithin(*this); element; element = Traversal<HTMLElement>::next(*element, this)) {
406         if (isHTMLObjectElement(*element) || isHTMLEmbedElement(*element))
407             return false;
408     }
409     return true;
410 }
411
412 bool HTMLObjectElement::containsJavaApplet() const
413 {
414     if (MIMETypeRegistry::isJavaAppletMIMEType(getAttribute(typeAttr)))
415         return true;
416
417     for (HTMLElement* child = Traversal<HTMLElement>::firstChild(*this); child; child = Traversal<HTMLElement>::nextSibling(*child)) {
418         if (isHTMLParamElement(*child)
419                 && equalIgnoringCase(child->getNameAttribute(), "type")
420                 && MIMETypeRegistry::isJavaAppletMIMEType(child->getAttribute(valueAttr).string()))
421             return true;
422         if (isHTMLObjectElement(*child) && toHTMLObjectElement(*child).containsJavaApplet())
423             return true;
424         if (isHTMLAppletElement(*child))
425             return true;
426     }
427
428     return false;
429 }
430
431 void HTMLObjectElement::didMoveToNewDocument(Document& oldDocument)
432 {
433     FormAssociatedElement::didMoveToNewDocument(oldDocument);
434     HTMLPlugInElement::didMoveToNewDocument(oldDocument);
435 }
436
437 bool HTMLObjectElement::appendFormData(FormDataList& encoding, bool)
438 {
439     if (name().isEmpty())
440         return false;
441
442     Widget* widget = pluginWidget();
443     if (!widget || !widget->isPluginView())
444         return false;
445     String value;
446     if (!toPluginView(widget)->getFormValue(value))
447         return false;
448     encoding.appendData(name(), value);
449     return true;
450 }
451
452 HTMLFormElement* HTMLObjectElement::formOwner() const
453 {
454     return FormAssociatedElement::form();
455 }
456
457 bool HTMLObjectElement::isInteractiveContent() const
458 {
459     return fastHasAttribute(usemapAttr);
460 }
461
462 bool HTMLObjectElement::useFallbackContent() const
463 {
464     return HTMLPlugInElement::useFallbackContent() || m_useFallbackContent;
465 }
466
467 }