• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/NodeList.h"
32 #include "core/dom/Text.h"
33 #include "core/dom/shadow/ShadowRoot.h"
34 #include "core/events/ThreadLocalEventNames.h"
35 #include "core/fetch/ImageResource.h"
36 #include "core/html/FormDataList.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 
HTMLObjectElement(Document & document,HTMLFormElement * form,bool createdByParser)52 inline HTMLObjectElement::HTMLObjectElement(Document& document, HTMLFormElement* form, bool createdByParser)
53     : HTMLPlugInElement(objectTag, document, createdByParser, ShouldNotPreferPlugInsForImages)
54     , m_useFallbackContent(false)
55 {
56     setForm(form ? form : findFormAncestor());
57     ScriptWrappable::init(this);
58 }
59 
~HTMLObjectElement()60 inline HTMLObjectElement::~HTMLObjectElement()
61 {
62     setForm(0);
63 }
64 
create(Document & document,HTMLFormElement * form,bool createdByParser)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 
existingRenderWidget() const72 RenderWidget* HTMLObjectElement::existingRenderWidget() const
73 {
74     return renderPart(); // This will return 0 if the renderer is not a RenderPart.
75 }
76 
isPresentationAttribute(const QualifiedName & name) const77 bool HTMLObjectElement::isPresentationAttribute(const QualifiedName& name) const
78 {
79     if (name == borderAttr)
80         return true;
81     return HTMLPlugInElement::isPresentationAttribute(name);
82 }
83 
collectStyleForPresentationAttribute(const QualifiedName & name,const AtomicString & value,MutableStylePropertySet * style)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 
parseAttribute(const QualifiedName & name,const AtomicString & value)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         if (renderer())
102             setNeedsWidgetUpdate(true);
103     } else if (name == dataAttr) {
104         m_url = stripLeadingAndTrailingHTMLSpaces(value);
105         if (renderer()) {
106             setNeedsWidgetUpdate(true);
107             if (isImageType()) {
108                 if (!m_imageLoader)
109                     m_imageLoader = adoptPtr(new HTMLImageLoader(this));
110                 m_imageLoader->updateFromElementIgnoringPreviousError();
111             }
112         }
113     } else if (name == classidAttr) {
114         m_classId = value;
115         if (renderer())
116             setNeedsWidgetUpdate(true);
117     } else if (name == onbeforeloadAttr) {
118         setAttributeEventListener(EventTypeNames::beforeload, createAttributeEventListener(this, name, value));
119     } else {
120         HTMLPlugInElement::parseAttribute(name, value);
121     }
122 }
123 
mapDataParamToSrc(Vector<String> * paramNames,Vector<String> * paramValues)124 static void mapDataParamToSrc(Vector<String>* paramNames, Vector<String>* paramValues)
125 {
126     // Some plugins don't understand the "data" attribute of the OBJECT tag (i.e. Real and WMP
127     // require "src" attribute).
128     int srcIndex = -1, dataIndex = -1;
129     for (unsigned int i = 0; i < paramNames->size(); ++i) {
130         if (equalIgnoringCase((*paramNames)[i], "src"))
131             srcIndex = i;
132         else if (equalIgnoringCase((*paramNames)[i], "data"))
133             dataIndex = i;
134     }
135 
136     if (srcIndex == -1 && dataIndex != -1) {
137         paramNames->append("src");
138         paramValues->append((*paramValues)[dataIndex]);
139     }
140 }
141 
142 // FIXME: This function should not deal with url or serviceType!
parametersForPlugin(Vector<String> & paramNames,Vector<String> & paramValues,String & url,String & serviceType)143 void HTMLObjectElement::parametersForPlugin(Vector<String>& paramNames, Vector<String>& paramValues, String& url, String& serviceType)
144 {
145     HashSet<StringImpl*, CaseFoldingHash> uniqueParamNames;
146     String urlParameter;
147 
148     // Scan the PARAM children and store their name/value pairs.
149     // Get the URL and type from the params if we don't already have them.
150     for (Node* child = firstChild(); child; child = child->nextSibling()) {
151         if (!child->hasTagName(paramTag))
152             continue;
153 
154         HTMLParamElement* p = toHTMLParamElement(child);
155         String name = p->name();
156         if (name.isEmpty())
157             continue;
158 
159         uniqueParamNames.add(name.impl());
160         paramNames.append(p->name());
161         paramValues.append(p->value());
162 
163         // FIXME: url adjustment does not belong in this function.
164         if (url.isEmpty() && urlParameter.isEmpty() && (equalIgnoringCase(name, "src") || equalIgnoringCase(name, "movie") || equalIgnoringCase(name, "code") || equalIgnoringCase(name, "url")))
165             urlParameter = stripLeadingAndTrailingHTMLSpaces(p->value());
166         // FIXME: serviceType calculation does not belong in this function.
167         if (serviceType.isEmpty() && equalIgnoringCase(name, "type")) {
168             serviceType = p->value();
169             size_t pos = serviceType.find(";");
170             if (pos != kNotFound)
171                 serviceType = serviceType.left(pos);
172         }
173     }
174 
175     // When OBJECT is used for an applet via Sun's Java plugin, the CODEBASE attribute in the tag
176     // points to the Java plugin itself (an ActiveX component) while the actual applet CODEBASE is
177     // in a PARAM tag. See <http://java.sun.com/products/plugin/1.2/docs/tags.html>. This means
178     // we have to explicitly suppress the tag's CODEBASE attribute if there is none in a PARAM,
179     // else our Java plugin will misinterpret it. [4004531]
180     String codebase;
181     if (MIMETypeRegistry::isJavaAppletMIMEType(serviceType)) {
182         codebase = "codebase";
183         uniqueParamNames.add(codebase.impl()); // pretend we found it in a PARAM already
184     }
185 
186     // Turn the attributes of the <object> element into arrays, but don't override <param> values.
187     if (hasAttributes()) {
188         for (unsigned i = 0; i < attributeCount(); ++i) {
189             const Attribute* attribute = attributeItem(i);
190             const AtomicString& name = attribute->name().localName();
191             if (!uniqueParamNames.contains(name.impl())) {
192                 paramNames.append(name.string());
193                 paramValues.append(attribute->value().string());
194             }
195         }
196     }
197 
198     mapDataParamToSrc(&paramNames, &paramValues);
199 
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 plug-in.
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;
209     }
210 }
211 
212 
hasFallbackContent() const213 bool HTMLObjectElement::hasFallbackContent() const
214 {
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 (!child->hasTagName(paramTag))
221             return true;
222     }
223     return false;
224 }
225 
shouldAllowQuickTimeClassIdQuirk()226 bool HTMLObjectElement::shouldAllowQuickTimeClassIdQuirk()
227 {
228     // This site-specific hack maintains compatibility with Mac OS X Wiki Server,
229     // which embeds QuickTime movies using an object tag containing QuickTime's
230     // ActiveX classid. Treat this classid as valid only if OS X Server's unique
231     // 'generator' meta tag is present. Only apply this quirk if there is no
232     // fallback content, which ensures the quirk will disable itself if Wiki
233     // Server is updated to generate an alternate embed tag as fallback content.
234     if (!document().settings()
235         || !document().settings()->needsSiteSpecificQuirks()
236         || hasFallbackContent()
237         || !equalIgnoringCase(classId(), "clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B"))
238         return false;
239 
240     RefPtr<NodeList> metaElements = document().getElementsByTagName(HTMLNames::metaTag.localName());
241     unsigned length = metaElements->length();
242     for (unsigned i = 0; i < length; ++i) {
243         ASSERT(metaElements->item(i)->isHTMLElement());
244         HTMLMetaElement* metaElement = toHTMLMetaElement(metaElements->item(i));
245         if (equalIgnoringCase(metaElement->name(), "generator") && metaElement->content().startsWith("Mac OS X Server Web Services Server", false))
246             return true;
247     }
248 
249     return false;
250 }
251 
hasValidClassId()252 bool HTMLObjectElement::hasValidClassId()
253 {
254     if (MIMETypeRegistry::isJavaAppletMIMEType(m_serviceType) && classId().startsWith("java:", false))
255         return true;
256 
257     if (shouldAllowQuickTimeClassIdQuirk())
258         return true;
259 
260     // HTML5 says that fallback content should be rendered if a non-empty
261     // classid is specified for which the UA can't find a suitable plug-in.
262     return classId().isEmpty();
263 }
264 
265 // FIXME: This should be unified with HTMLEmbedElement::updateWidget and
266 // moved down into HTMLPluginElement.cpp
updateWidgetInternal()267 void HTMLObjectElement::updateWidgetInternal()
268 {
269     ASSERT(!renderEmbeddedObject()->showsUnavailablePluginIndicator());
270     ASSERT(needsWidgetUpdate());
271     setNeedsWidgetUpdate(false);
272     // FIXME: This should ASSERT isFinishedParsingChildren() instead.
273     if (!isFinishedParsingChildren())
274         return;
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         return;
281 
282     String url = this->url();
283     String serviceType = m_serviceType;
284 
285     // FIXME: These should be joined into a PluginParameters class.
286     Vector<String> paramNames;
287     Vector<String> paramValues;
288     parametersForPlugin(paramNames, paramValues, url, serviceType);
289 
290     // Note: url is modified above by parametersForPlugin.
291     if (!allowedToLoadFrameURL(url))
292         return;
293 
294     bool fallbackContent = hasFallbackContent();
295     renderEmbeddedObject()->setHasFallbackContent(fallbackContent);
296 
297     RefPtr<HTMLObjectElement> protect(this); // beforeload and plugin loading can make arbitrary DOM mutations.
298     bool beforeLoadAllowedLoad = dispatchBeforeLoadEvent(url);
299     if (!renderer()) // Do not load the plugin if beforeload removed this element or its renderer.
300         return;
301 
302     bool success = beforeLoadAllowedLoad && hasValidClassId() && requestObject(url, serviceType, paramNames, paramValues);
303     if (!success && fallbackContent)
304         renderFallbackContent();
305 }
306 
rendererIsNeeded(const RenderStyle & style)307 bool HTMLObjectElement::rendererIsNeeded(const RenderStyle& style)
308 {
309     // FIXME: This check should not be needed, detached documents never render!
310     if (!document().frame())
311         return false;
312     return HTMLPlugInElement::rendererIsNeeded(style);
313 }
314 
insertedInto(ContainerNode * insertionPoint)315 Node::InsertionNotificationRequest HTMLObjectElement::insertedInto(ContainerNode* insertionPoint)
316 {
317     HTMLPlugInElement::insertedInto(insertionPoint);
318     FormAssociatedElement::insertedInto(insertionPoint);
319     return InsertionDone;
320 }
321 
removedFrom(ContainerNode * insertionPoint)322 void HTMLObjectElement::removedFrom(ContainerNode* insertionPoint)
323 {
324     HTMLPlugInElement::removedFrom(insertionPoint);
325     FormAssociatedElement::removedFrom(insertionPoint);
326 }
327 
childrenChanged(bool changedByParser,Node * beforeChange,Node * afterChange,int childCountDelta)328 void HTMLObjectElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
329 {
330     if (inDocument() && !useFallbackContent()) {
331         setNeedsWidgetUpdate(true);
332         setNeedsStyleRecalc();
333     }
334     HTMLPlugInElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
335 }
336 
isURLAttribute(const Attribute & attribute) const337 bool HTMLObjectElement::isURLAttribute(const Attribute& attribute) const
338 {
339     return attribute.name() == codebaseAttr || attribute.name() == dataAttr
340         || (attribute.name() == usemapAttr && attribute.value().string()[0] != '#')
341         || HTMLPlugInElement::isURLAttribute(attribute);
342 }
343 
imageSourceURL() const344 const AtomicString HTMLObjectElement::imageSourceURL() const
345 {
346     return getAttribute(dataAttr);
347 }
348 
349 // FIXME: Remove this hack.
reattachFallbackContent()350 void HTMLObjectElement::reattachFallbackContent()
351 {
352     // This can happen inside of attach() in the middle of a recalcStyle so we need to
353     // reattach synchronously here.
354     if (document().inStyleRecalc())
355         reattach();
356     else
357         lazyReattachIfAttached();
358 }
359 
renderFallbackContent()360 void HTMLObjectElement::renderFallbackContent()
361 {
362     if (useFallbackContent())
363         return;
364 
365     if (!inDocument())
366         return;
367 
368     // Before we give up and use fallback content, check to see if this is a MIME type issue.
369     if (m_imageLoader && m_imageLoader->image() && m_imageLoader->image()->status() != Resource::LoadError) {
370         m_serviceType = m_imageLoader->image()->response().mimeType();
371         if (!isImageType()) {
372             // If we don't think we have an image type anymore, then clear the image from the loader.
373             m_imageLoader->setImage(0);
374             reattachFallbackContent();
375             return;
376         }
377     }
378 
379     m_useFallbackContent = true;
380 
381     // FIXME: Style gets recalculated which is suboptimal.
382     reattachFallbackContent();
383 }
384 
isExposed() const385 bool HTMLObjectElement::isExposed() const
386 {
387     // http://www.whatwg.org/specs/web-apps/current-work/#exposed
388     for (Node* ancestor = parentNode(); ancestor; ancestor = ancestor->parentNode()) {
389         if (ancestor->hasTagName(objectTag) && toHTMLObjectElement(ancestor)->isExposed())
390             return false;
391     }
392     for (Node* node = firstChild(); node; node = NodeTraversal::next(*node, this)) {
393         if (node->hasTagName(objectTag) || node->hasTagName(embedTag))
394             return false;
395     }
396     return true;
397 }
398 
containsJavaApplet() const399 bool HTMLObjectElement::containsJavaApplet() const
400 {
401     if (MIMETypeRegistry::isJavaAppletMIMEType(getAttribute(typeAttr)))
402         return true;
403 
404     for (Element* child = ElementTraversal::firstWithin(*this); child; child = ElementTraversal::nextSkippingChildren(*child, this)) {
405         if (child->hasTagName(paramTag)
406                 && equalIgnoringCase(child->getNameAttribute(), "type")
407                 && MIMETypeRegistry::isJavaAppletMIMEType(child->getAttribute(valueAttr).string()))
408             return true;
409         if (child->hasTagName(objectTag) && toHTMLObjectElement(child)->containsJavaApplet())
410             return true;
411         if (child->hasTagName(appletTag))
412             return true;
413     }
414 
415     return false;
416 }
417 
addSubresourceAttributeURLs(ListHashSet<KURL> & urls) const418 void HTMLObjectElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const
419 {
420     HTMLPlugInElement::addSubresourceAttributeURLs(urls);
421 
422     addSubresourceURL(urls, document().completeURL(getAttribute(dataAttr)));
423 
424     // FIXME: Passing a string that starts with "#" to the completeURL function does
425     // not seem like it would work. The image element has similar but not identical code.
426     const AtomicString& useMap = getAttribute(usemapAttr);
427     if (useMap.startsWith('#'))
428         addSubresourceURL(urls, document().completeURL(useMap));
429 }
430 
didMoveToNewDocument(Document & oldDocument)431 void HTMLObjectElement::didMoveToNewDocument(Document& oldDocument)
432 {
433     FormAssociatedElement::didMoveToNewDocument(oldDocument);
434     HTMLPlugInElement::didMoveToNewDocument(oldDocument);
435 }
436 
appendFormData(FormDataList & encoding,bool)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 
formOwner() const452 HTMLFormElement* HTMLObjectElement::formOwner() const
453 {
454     return FormAssociatedElement::form();
455 }
456 
isInteractiveContent() const457 bool HTMLObjectElement::isInteractiveContent() const
458 {
459     return fastHasAttribute(usemapAttr);
460 }
461 
useFallbackContent() const462 bool HTMLObjectElement::useFallbackContent() const
463 {
464     return HTMLPlugInElement::useFallbackContent() || m_useFallbackContent;
465 }
466 
467 }
468