Upstream version 5.34.104.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 "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/events/ThreadLocalEventNames.h"
34 #include "core/fetch/ImageResource.h"
35 #include "core/html/FormDataList.h"
36 #include "core/html/HTMLCollection.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/frame/Settings.h"
43 #include "core/plugins/PluginView.h"
44 #include "core/rendering/RenderEmbeddedObject.h"
45 #include "platform/MIMETypeRegistry.h"
46 #include "platform/Widget.h"
47
48 namespace WebCore {
49
50 using namespace HTMLNames;
51
52 inline HTMLObjectElement::HTMLObjectElement(Document& document, HTMLFormElement* form, bool createdByParser)
53     : HTMLPlugInElement(objectTag, document, createdByParser, ShouldNotPreferPlugInsForImages)
54     , m_useFallbackContent(false)
55 {
56     ScriptWrappable::init(this);
57     associateByParser(form);
58 }
59
60 inline HTMLObjectElement::~HTMLObjectElement()
61 {
62     setForm(0);
63 }
64
65 PassRefPtr<HTMLObjectElement> HTMLObjectElement::create(Document& document, HTMLFormElement* form, bool createdByParser)
66 {
67     RefPtr<HTMLObjectElement> element = adoptRef(new HTMLObjectElement(document, form, createdByParser));
68     element->ensureUserAgentShadowRoot();
69     return element.release();
70 }
71
72 RenderWidget* HTMLObjectElement::existingRenderWidget() const
73 {
74     return renderPart(); // This will return 0 if the renderer is not a RenderPart.
75 }
76
77 bool HTMLObjectElement::isPresentationAttribute(const QualifiedName& name) const
78 {
79     if (name == borderAttr)
80         return true;
81     return HTMLPlugInElement::isPresentationAttribute(name);
82 }
83
84 void HTMLObjectElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style)
85 {
86     if (name == borderAttr)
87         applyBorderAttributeToStyle(value, style);
88     else
89         HTMLPlugInElement::collectStyleForPresentationAttribute(name, value, style);
90 }
91
92 void HTMLObjectElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
93 {
94     if (name == formAttr)
95         formAttributeChanged();
96     else if (name == typeAttr) {
97         m_serviceType = value.lower();
98         size_t pos = m_serviceType.find(";");
99         if (pos != kNotFound)
100             m_serviceType = m_serviceType.left(pos);
101         reloadPluginOnAttributeChange(name);
102     } else if (name == dataAttr) {
103         m_url = stripLeadingAndTrailingHTMLSpaces(value);
104         if (renderer() && isImageType()) {
105             setNeedsWidgetUpdate(true);
106             if (!m_imageLoader)
107                 m_imageLoader = adoptPtr(new HTMLImageLoader(this));
108             m_imageLoader->updateFromElementIgnoringPreviousError();
109         } else {
110             reloadPluginOnAttributeChange(name);
111         }
112     } else if (name == classidAttr) {
113         m_classId = value;
114         reloadPluginOnAttributeChange(name);
115     } else if (name == onbeforeloadAttr) {
116         setAttributeEventListener(EventTypeNames::beforeload, createAttributeEventListener(this, name, value));
117     } else {
118         HTMLPlugInElement::parseAttribute(name, value);
119     }
120 }
121
122 static void mapDataParamToSrc(Vector<String>* paramNames, Vector<String>* paramValues)
123 {
124     // Some plugins don't understand the "data" attribute of the OBJECT tag (i.e. Real and WMP
125     // require "src" attribute).
126     int srcIndex = -1, dataIndex = -1;
127     for (unsigned int i = 0; i < paramNames->size(); ++i) {
128         if (equalIgnoringCase((*paramNames)[i], "src"))
129             srcIndex = i;
130         else if (equalIgnoringCase((*paramNames)[i], "data"))
131             dataIndex = i;
132     }
133
134     if (srcIndex == -1 && dataIndex != -1) {
135         paramNames->append("src");
136         paramValues->append((*paramValues)[dataIndex]);
137     }
138 }
139
140 // FIXME: This function should not deal with url or serviceType!
141 void HTMLObjectElement::parametersForPlugin(Vector<String>& paramNames, Vector<String>& paramValues, String& url, String& serviceType)
142 {
143     HashSet<StringImpl*, CaseFoldingHash> uniqueParamNames;
144     String urlParameter;
145
146     // Scan the PARAM children and store their name/value pairs.
147     // Get the URL and type from the params if we don't already have them.
148     for (Node* child = firstChild(); child; child = child->nextSibling()) {
149         if (!child->hasTagName(paramTag))
150             continue;
151
152         HTMLParamElement* p = toHTMLParamElement(child);
153         String name = p->name();
154         if (name.isEmpty())
155             continue;
156
157         uniqueParamNames.add(name.impl());
158         paramNames.append(p->name());
159         paramValues.append(p->value());
160
161         // FIXME: url adjustment does not belong in this function.
162         if (url.isEmpty() && urlParameter.isEmpty() && (equalIgnoringCase(name, "src") || equalIgnoringCase(name, "movie") || equalIgnoringCase(name, "code") || equalIgnoringCase(name, "url")))
163             urlParameter = stripLeadingAndTrailingHTMLSpaces(p->value());
164         // FIXME: serviceType calculation does not belong in this function.
165         if (serviceType.isEmpty() && equalIgnoringCase(name, "type")) {
166             serviceType = p->value();
167             size_t pos = serviceType.find(";");
168             if (pos != kNotFound)
169                 serviceType = serviceType.left(pos);
170         }
171     }
172
173     // When OBJECT is used for an applet via Sun's Java plugin, the CODEBASE attribute in the tag
174     // points to the Java plugin itself (an ActiveX component) while the actual applet CODEBASE is
175     // in a PARAM tag. See <http://java.sun.com/products/plugin/1.2/docs/tags.html>. This means
176     // we have to explicitly suppress the tag's CODEBASE attribute if there is none in a PARAM,
177     // else our Java plugin will misinterpret it. [4004531]
178     String codebase;
179     if (MIMETypeRegistry::isJavaAppletMIMEType(serviceType)) {
180         codebase = "codebase";
181         uniqueParamNames.add(codebase.impl()); // pretend we found it in a PARAM already
182     }
183
184     // Turn the attributes of the <object> element into arrays, but don't override <param> values.
185     if (hasAttributes()) {
186         for (unsigned i = 0; i < attributeCount(); ++i) {
187             const Attribute* attribute = attributeItem(i);
188             const AtomicString& name = attribute->name().localName();
189             if (!uniqueParamNames.contains(name.impl())) {
190                 paramNames.append(name.string());
191                 paramValues.append(attribute->value().string());
192             }
193         }
194     }
195
196     mapDataParamToSrc(&paramNames, &paramValues);
197
198     // HTML5 says that an object resource's URL is specified by the object's data
199     // attribute, not by a param element. However, for compatibility, allow the
200     // resource's URL to be given by a param named "src", "movie", "code" or "url"
201     // if we know that resource points to a plug-in.
202     if (url.isEmpty() && !urlParameter.isEmpty()) {
203         KURL completedURL = document().completeURL(urlParameter);
204         bool useFallback;
205         if (shouldUsePlugin(completedURL, serviceType, false, useFallback))
206             url = urlParameter;
207     }
208 }
209
210
211 bool HTMLObjectElement::hasFallbackContent() const
212 {
213     for (Node* child = firstChild(); child; child = child->nextSibling()) {
214         // Ignore whitespace-only text, and <param> tags, any other content is fallback content.
215         if (child->isTextNode()) {
216             if (!toText(child)->containsOnlyWhitespace())
217                 return true;
218         } else if (!child->hasTagName(paramTag))
219             return true;
220     }
221     return false;
222 }
223
224 bool HTMLObjectElement::shouldAllowQuickTimeClassIdQuirk()
225 {
226     // This site-specific hack maintains compatibility with Mac OS X Wiki Server,
227     // which embeds QuickTime movies using an object tag containing QuickTime's
228     // ActiveX classid. Treat this classid as valid only if OS X Server's unique
229     // 'generator' meta tag is present. Only apply this quirk if there is no
230     // fallback content, which ensures the quirk will disable itself if Wiki
231     // Server is updated to generate an alternate embed tag as fallback content.
232     if (!document().settings()
233         || !document().settings()->needsSiteSpecificQuirks()
234         || hasFallbackContent()
235         || !equalIgnoringCase(classId(), "clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B"))
236         return false;
237
238     RefPtr<HTMLCollection> metaElements = document().getElementsByTagName(HTMLNames::metaTag.localName());
239     unsigned length = metaElements->length();
240     for (unsigned i = 0; i < length; ++i) {
241         ASSERT(metaElements->item(i)->isHTMLElement());
242         HTMLMetaElement* metaElement = toHTMLMetaElement(metaElements->item(i));
243         if (equalIgnoringCase(metaElement->name(), "generator") && metaElement->content().startsWith("Mac OS X Server Web Services Server", false))
244             return true;
245     }
246
247     return false;
248 }
249
250 bool HTMLObjectElement::hasValidClassId()
251 {
252     if (MIMETypeRegistry::isJavaAppletMIMEType(m_serviceType) && classId().startsWith("java:", false))
253         return true;
254
255     if (shouldAllowQuickTimeClassIdQuirk())
256         return true;
257
258     // HTML5 says that fallback content should be rendered if a non-empty
259     // classid is specified for which the UA can't find a suitable plug-in.
260     return classId().isEmpty();
261 }
262
263 void HTMLObjectElement::reloadPluginOnAttributeChange(const QualifiedName& name)
264 {
265     // Following,
266     //   http://www.whatwg.org/specs/web-apps/current-work/#the-object-element
267     //   (Enumerated list below "Whenever one of the following conditions occur:")
268     //
269     // the updating of certain attributes should bring about "redetermination"
270     // of what the element contains.
271     bool needsInvalidation;
272     if (name == typeAttr) {
273         needsInvalidation = !fastHasAttribute(classidAttr) && !fastHasAttribute(dataAttr);
274     } else if (name == dataAttr) {
275         needsInvalidation = !fastHasAttribute(classidAttr);
276     } else if (name == classidAttr) {
277         needsInvalidation = true;
278     } else {
279         ASSERT_NOT_REACHED();
280         needsInvalidation = false;
281     }
282     setNeedsWidgetUpdate(true);
283     if (needsInvalidation)
284         setNeedsStyleRecalc(SubtreeStyleChange);
285 }
286
287 // FIXME: This should be unified with HTMLEmbedElement::updateWidget and
288 // moved down into HTMLPluginElement.cpp
289 void HTMLObjectElement::updateWidgetInternal()
290 {
291     ASSERT(!renderEmbeddedObject()->showsUnavailablePluginIndicator());
292     ASSERT(needsWidgetUpdate());
293     setNeedsWidgetUpdate(false);
294     // FIXME: This should ASSERT isFinishedParsingChildren() instead.
295     if (!isFinishedParsingChildren()) {
296         dispatchErrorEvent();
297         return;
298     }
299
300     // FIXME: I'm not sure it's ever possible to get into updateWidget during a
301     // removal, but just in case we should avoid loading the frame to prevent
302     // security bugs.
303     if (!SubframeLoadingDisabler::canLoadFrame(*this)) {
304         dispatchErrorEvent();
305         return;
306     }
307
308     String url = this->url();
309     String serviceType = m_serviceType;
310
311     // FIXME: These should be joined into a PluginParameters class.
312     Vector<String> paramNames;
313     Vector<String> paramValues;
314     parametersForPlugin(paramNames, paramValues, url, serviceType);
315
316     // Note: url is modified above by parametersForPlugin.
317     if (!allowedToLoadFrameURL(url)) {
318         dispatchErrorEvent();
319         return;
320     }
321
322     bool fallbackContent = hasFallbackContent();
323     renderEmbeddedObject()->setHasFallbackContent(fallbackContent);
324
325     RefPtr<HTMLObjectElement> protect(this); // beforeload and plugin loading can make arbitrary DOM mutations.
326     bool beforeLoadAllowedLoad = dispatchBeforeLoadEvent(url);
327     if (!renderer()) // Do not load the plugin if beforeload removed this element or its renderer.
328         return;
329
330     if (!beforeLoadAllowedLoad || !hasValidClassId() || !requestObject(url, serviceType, paramNames, paramValues)) {
331         if (!url.isEmpty())
332             dispatchErrorEvent();
333         if (fallbackContent)
334             renderFallbackContent();
335     }
336 }
337
338 bool HTMLObjectElement::rendererIsNeeded(const RenderStyle& style)
339 {
340     // FIXME: This check should not be needed, detached documents never render!
341     if (!document().frame())
342         return false;
343     return HTMLPlugInElement::rendererIsNeeded(style);
344 }
345
346 Node::InsertionNotificationRequest HTMLObjectElement::insertedInto(ContainerNode* insertionPoint)
347 {
348     HTMLPlugInElement::insertedInto(insertionPoint);
349     FormAssociatedElement::insertedInto(insertionPoint);
350     return InsertionDone;
351 }
352
353 void HTMLObjectElement::removedFrom(ContainerNode* insertionPoint)
354 {
355     HTMLPlugInElement::removedFrom(insertionPoint);
356     FormAssociatedElement::removedFrom(insertionPoint);
357 }
358
359 void HTMLObjectElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
360 {
361     if (inDocument() && !useFallbackContent()) {
362         setNeedsWidgetUpdate(true);
363         setNeedsStyleRecalc(SubtreeStyleChange);
364     }
365     HTMLPlugInElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
366 }
367
368 bool HTMLObjectElement::isURLAttribute(const Attribute& attribute) const
369 {
370     return attribute.name() == codebaseAttr || attribute.name() == dataAttr
371         || (attribute.name() == usemapAttr && attribute.value().string()[0] != '#')
372         || HTMLPlugInElement::isURLAttribute(attribute);
373 }
374
375 const AtomicString HTMLObjectElement::imageSourceURL() const
376 {
377     return getAttribute(dataAttr);
378 }
379
380 // FIXME: Remove this hack.
381 void HTMLObjectElement::reattachFallbackContent()
382 {
383     // This can happen inside of attach() in the middle of a recalcStyle so we need to
384     // reattach synchronously here.
385     if (document().inStyleRecalc())
386         reattach();
387     else
388         lazyReattachIfAttached();
389 }
390
391 void HTMLObjectElement::renderFallbackContent()
392 {
393     if (useFallbackContent())
394         return;
395
396     if (!inDocument())
397         return;
398
399     // Before we give up and use fallback content, check to see if this is a MIME type issue.
400     if (m_imageLoader && m_imageLoader->image() && m_imageLoader->image()->status() != Resource::LoadError) {
401         m_serviceType = m_imageLoader->image()->response().mimeType();
402         if (!isImageType()) {
403             // If we don't think we have an image type anymore, then clear the image from the loader.
404             m_imageLoader->setImage(0);
405             reattachFallbackContent();
406             return;
407         }
408     }
409
410     m_useFallbackContent = true;
411
412     // FIXME: Style gets recalculated which is suboptimal.
413     reattachFallbackContent();
414 }
415
416 bool HTMLObjectElement::isExposed() const
417 {
418     // http://www.whatwg.org/specs/web-apps/current-work/#exposed
419     for (Node* ancestor = parentNode(); ancestor; ancestor = ancestor->parentNode()) {
420         if (ancestor->hasTagName(objectTag) && toHTMLObjectElement(ancestor)->isExposed())
421             return false;
422     }
423     for (Node* node = firstChild(); node; node = NodeTraversal::next(*node, this)) {
424         if (node->hasTagName(objectTag) || node->hasTagName(embedTag))
425             return false;
426     }
427     return true;
428 }
429
430 bool HTMLObjectElement::containsJavaApplet() const
431 {
432     if (MIMETypeRegistry::isJavaAppletMIMEType(getAttribute(typeAttr)))
433         return true;
434
435     for (Element* child = ElementTraversal::firstWithin(*this); child; child = ElementTraversal::nextSkippingChildren(*child, this)) {
436         if (child->hasTagName(paramTag)
437                 && equalIgnoringCase(child->getNameAttribute(), "type")
438                 && MIMETypeRegistry::isJavaAppletMIMEType(child->getAttribute(valueAttr).string()))
439             return true;
440         if (child->hasTagName(objectTag) && toHTMLObjectElement(child)->containsJavaApplet())
441             return true;
442         if (child->hasTagName(appletTag))
443             return true;
444     }
445
446     return false;
447 }
448
449 void HTMLObjectElement::didMoveToNewDocument(Document& oldDocument)
450 {
451     FormAssociatedElement::didMoveToNewDocument(oldDocument);
452     HTMLPlugInElement::didMoveToNewDocument(oldDocument);
453 }
454
455 bool HTMLObjectElement::appendFormData(FormDataList& encoding, bool)
456 {
457     if (name().isEmpty())
458         return false;
459
460     Widget* widget = pluginWidget();
461     if (!widget || !widget->isPluginView())
462         return false;
463     String value;
464     if (!toPluginView(widget)->getFormValue(value))
465         return false;
466     encoding.appendData(name(), value);
467     return true;
468 }
469
470 HTMLFormElement* HTMLObjectElement::formOwner() const
471 {
472     return FormAssociatedElement::form();
473 }
474
475 bool HTMLObjectElement::isInteractiveContent() const
476 {
477     return fastHasAttribute(usemapAttr);
478 }
479
480 bool HTMLObjectElement::useFallbackContent() const
481 {
482     return HTMLPlugInElement::useFallbackContent() || m_useFallbackContent;
483 }
484
485 }