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 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 "HTMLObjectElement.h"
26
27 #include "CSSHelper.h"
28 #include "EventNames.h"
29 #include "ExceptionCode.h"
30 #include "Frame.h"
31 #include "HTMLDocument.h"
32 #include "HTMLFormElement.h"
33 #include "HTMLImageLoader.h"
34 #include "HTMLNames.h"
35 #include "ScriptEventListener.h"
36 #include "MIMETypeRegistry.h"
37 #include "MappedAttribute.h"
38 #include "RenderEmbeddedObject.h"
39 #include "RenderImage.h"
40 #include "RenderWidget.h"
41 #include "ScriptController.h"
42 #include "Text.h"
43
44 namespace WebCore {
45
46 using namespace HTMLNames;
47
HTMLObjectElement(const QualifiedName & tagName,Document * document,bool createdByParser)48 inline HTMLObjectElement::HTMLObjectElement(const QualifiedName& tagName, Document* document, bool createdByParser)
49 : HTMLPlugInImageElement(tagName, document)
50 , m_docNamedItem(true)
51 , m_needWidgetUpdate(!createdByParser)
52 , m_useFallbackContent(false)
53 {
54 ASSERT(hasTagName(objectTag));
55 }
56
create(const QualifiedName & tagName,Document * document,bool createdByParser)57 PassRefPtr<HTMLObjectElement> HTMLObjectElement::create(const QualifiedName& tagName, Document* document, bool createdByParser)
58 {
59 return adoptRef(new HTMLObjectElement(tagName, document, createdByParser));
60 }
61
renderWidgetForJSBindings() const62 RenderWidget* HTMLObjectElement::renderWidgetForJSBindings() const
63 {
64 document()->updateLayoutIgnorePendingStylesheets();
65 if (!renderer() || !renderer()->isWidget())
66 return 0;
67 return toRenderWidget(renderer());
68 }
69
parseMappedAttribute(MappedAttribute * attr)70 void HTMLObjectElement::parseMappedAttribute(MappedAttribute *attr)
71 {
72 String val = attr->value();
73 int pos;
74 if (attr->name() == typeAttr) {
75 m_serviceType = val.lower();
76 pos = m_serviceType.find(";");
77 if (pos != -1)
78 m_serviceType = m_serviceType.left(pos);
79 if (renderer())
80 m_needWidgetUpdate = true;
81 if (!isImageType() && m_imageLoader)
82 m_imageLoader.clear();
83 } else if (attr->name() == dataAttr) {
84 m_url = deprecatedParseURL(val);
85 if (renderer())
86 m_needWidgetUpdate = true;
87 if (renderer() && isImageType()) {
88 if (!m_imageLoader)
89 m_imageLoader.set(new HTMLImageLoader(this));
90 m_imageLoader->updateFromElementIgnoringPreviousError();
91 }
92 } else if (attr->name() == classidAttr) {
93 m_classId = val;
94 if (renderer())
95 m_needWidgetUpdate = true;
96 } else if (attr->name() == onloadAttr)
97 setAttributeEventListener(eventNames().loadEvent, createAttributeEventListener(this, attr));
98 else if (attr->name() == onbeforeloadAttr)
99 setAttributeEventListener(eventNames().beforeloadEvent, createAttributeEventListener(this, attr));
100 else if (attr->name() == nameAttr) {
101 const AtomicString& newName = attr->value();
102 if (isDocNamedItem() && inDocument() && document()->isHTMLDocument()) {
103 HTMLDocument* document = static_cast<HTMLDocument*>(this->document());
104 document->removeNamedItem(m_name);
105 document->addNamedItem(newName);
106 }
107 m_name = newName;
108 } else if (attr->name() == idAttributeName()) {
109 const AtomicString& newId = attr->value();
110 if (isDocNamedItem() && inDocument() && document()->isHTMLDocument()) {
111 HTMLDocument* document = static_cast<HTMLDocument*>(this->document());
112 document->removeExtraNamedItem(m_id);
113 document->addExtraNamedItem(newId);
114 }
115 m_id = newId;
116 // also call superclass
117 HTMLPlugInElement::parseMappedAttribute(attr);
118 } else
119 HTMLPlugInElement::parseMappedAttribute(attr);
120 }
121
rendererIsNeeded(RenderStyle * style)122 bool HTMLObjectElement::rendererIsNeeded(RenderStyle* style)
123 {
124 Frame* frame = document()->frame();
125 if (!frame)
126 return false;
127
128 // Temporary Workaround for Gears plugin - see bug 24215 for details and bug 24346 to track removal.
129 // Gears expects the plugin to be instantiated even if display:none is set
130 // for the object element.
131 bool isGearsPlugin = equalIgnoringCase(getAttribute(typeAttr), "application/x-googlegears");
132 return isGearsPlugin || HTMLPlugInElement::rendererIsNeeded(style);
133 }
134
createRenderer(RenderArena * arena,RenderStyle * style)135 RenderObject *HTMLObjectElement::createRenderer(RenderArena* arena, RenderStyle* style)
136 {
137 if (m_useFallbackContent)
138 return RenderObject::createObject(this, style);
139 if (isImageType())
140 return new (arena) RenderImage(this);
141 return new (arena) RenderEmbeddedObject(this);
142 }
143
attach()144 void HTMLObjectElement::attach()
145 {
146 bool isImage = isImageType();
147
148 if (!isImage)
149 queuePostAttachCallback(&HTMLPlugInElement::updateWidgetCallback, this);
150
151 HTMLPlugInElement::attach();
152
153 if (isImage && renderer() && !m_useFallbackContent) {
154 if (!m_imageLoader)
155 m_imageLoader.set(new HTMLImageLoader(this));
156 m_imageLoader->updateFromElement();
157 // updateForElement() may have changed us to use fallback content and called detach() and attach().
158 if (m_useFallbackContent)
159 return;
160
161 if (renderer())
162 toRenderImage(renderer())->setCachedImage(m_imageLoader->image());
163 }
164 }
165
updateWidget()166 void HTMLObjectElement::updateWidget()
167 {
168 document()->updateStyleIfNeeded();
169 if (m_needWidgetUpdate && renderer() && !m_useFallbackContent && !isImageType())
170 toRenderEmbeddedObject(renderer())->updateWidget(true);
171 }
172
finishParsingChildren()173 void HTMLObjectElement::finishParsingChildren()
174 {
175 HTMLPlugInElement::finishParsingChildren();
176 if (!m_useFallbackContent) {
177 m_needWidgetUpdate = true;
178 if (inDocument())
179 setNeedsStyleRecalc();
180 }
181 }
182
detach()183 void HTMLObjectElement::detach()
184 {
185 if (attached() && renderer() && !m_useFallbackContent)
186 // Update the widget the next time we attach (detaching destroys the plugin).
187 m_needWidgetUpdate = true;
188 HTMLPlugInElement::detach();
189 }
190
insertedIntoDocument()191 void HTMLObjectElement::insertedIntoDocument()
192 {
193 if (isDocNamedItem() && document()->isHTMLDocument()) {
194 HTMLDocument* document = static_cast<HTMLDocument*>(this->document());
195 document->addNamedItem(m_name);
196 document->addExtraNamedItem(m_id);
197 }
198
199 HTMLPlugInElement::insertedIntoDocument();
200 }
201
removedFromDocument()202 void HTMLObjectElement::removedFromDocument()
203 {
204 if (isDocNamedItem() && document()->isHTMLDocument()) {
205 HTMLDocument* document = static_cast<HTMLDocument*>(this->document());
206 document->removeNamedItem(m_name);
207 document->removeExtraNamedItem(m_id);
208 }
209
210 HTMLPlugInElement::removedFromDocument();
211 }
212
recalcStyle(StyleChange ch)213 void HTMLObjectElement::recalcStyle(StyleChange ch)
214 {
215 if (!m_useFallbackContent && m_needWidgetUpdate && renderer() && !isImageType()) {
216 detach();
217 attach();
218 }
219 HTMLPlugInElement::recalcStyle(ch);
220 }
221
childrenChanged(bool changedByParser,Node * beforeChange,Node * afterChange,int childCountDelta)222 void HTMLObjectElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
223 {
224 updateDocNamedItem();
225 if (inDocument() && !m_useFallbackContent) {
226 m_needWidgetUpdate = true;
227 setNeedsStyleRecalc();
228 }
229 HTMLPlugInElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
230 }
231
isURLAttribute(Attribute * attr) const232 bool HTMLObjectElement::isURLAttribute(Attribute *attr) const
233 {
234 return (attr->name() == dataAttr || (attr->name() == usemapAttr && attr->value().string()[0] != '#'));
235 }
236
imageSourceAttributeName() const237 const QualifiedName& HTMLObjectElement::imageSourceAttributeName() const
238 {
239 return dataAttr;
240 }
241
renderFallbackContent()242 void HTMLObjectElement::renderFallbackContent()
243 {
244 if (m_useFallbackContent)
245 return;
246
247 // Before we give up and use fallback content, check to see if this is a MIME type issue.
248 if (m_imageLoader && m_imageLoader->image()) {
249 m_serviceType = m_imageLoader->image()->response().mimeType();
250 if (!isImageType()) {
251 // If we don't think we have an image type anymore, then ditch the image loader.
252 m_imageLoader.clear();
253 detach();
254 attach();
255 return;
256 }
257 }
258
259 // Mark ourselves as using the fallback content.
260 m_useFallbackContent = true;
261
262 // Now do a detach and reattach.
263 // FIXME: Style gets recalculated which is suboptimal.
264 detach();
265 attach();
266 }
267
updateDocNamedItem()268 void HTMLObjectElement::updateDocNamedItem()
269 {
270 // The rule is "<object> elements with no children other than
271 // <param> elements, unknown elements and whitespace can be
272 // found by name in a document, and other <object> elements cannot."
273 bool wasNamedItem = m_docNamedItem;
274 bool isNamedItem = true;
275 Node* child = firstChild();
276 while (child && isNamedItem) {
277 if (child->isElementNode()) {
278 Element* element = static_cast<Element*>(child);
279 if (HTMLElement::isRecognizedTagName(element->tagQName()) && !element->hasTagName(paramTag))
280 isNamedItem = false;
281 } else if (child->isTextNode()) {
282 if (!static_cast<Text*>(child)->containsOnlyWhitespace())
283 isNamedItem = false;
284 } else
285 isNamedItem = false;
286 child = child->nextSibling();
287 }
288 if (isNamedItem != wasNamedItem && document()->isHTMLDocument()) {
289 HTMLDocument* document = static_cast<HTMLDocument*>(this->document());
290 if (isNamedItem) {
291 document->addNamedItem(m_name);
292 document->addExtraNamedItem(m_id);
293 } else {
294 document->removeNamedItem(m_name);
295 document->removeExtraNamedItem(m_id);
296 }
297 }
298 m_docNamedItem = isNamedItem;
299 }
300
declare() const301 bool HTMLObjectElement::declare() const
302 {
303 return !getAttribute(declareAttr).isNull();
304 }
305
setDeclare(bool declare)306 void HTMLObjectElement::setDeclare(bool declare)
307 {
308 setAttribute(declareAttr, declare ? "" : 0);
309 }
310
hspace() const311 int HTMLObjectElement::hspace() const
312 {
313 return getAttribute(hspaceAttr).toInt();
314 }
315
setHspace(int value)316 void HTMLObjectElement::setHspace(int value)
317 {
318 setAttribute(hspaceAttr, String::number(value));
319 }
320
vspace() const321 int HTMLObjectElement::vspace() const
322 {
323 return getAttribute(vspaceAttr).toInt();
324 }
325
setVspace(int value)326 void HTMLObjectElement::setVspace(int value)
327 {
328 setAttribute(vspaceAttr, String::number(value));
329 }
330
containsJavaApplet() const331 bool HTMLObjectElement::containsJavaApplet() const
332 {
333 if (MIMETypeRegistry::isJavaAppletMIMEType(getAttribute(typeAttr)))
334 return true;
335
336 for (Element* child = firstElementChild(); child; child = child->nextElementSibling()) {
337 if (child->hasTagName(paramTag)
338 && equalIgnoringCase(child->getAttribute(nameAttr), "type")
339 && MIMETypeRegistry::isJavaAppletMIMEType(child->getAttribute(valueAttr).string()))
340 return true;
341 if (child->hasTagName(objectTag)
342 && static_cast<HTMLObjectElement*>(child)->containsJavaApplet())
343 return true;
344 if (child->hasTagName(appletTag))
345 return true;
346 }
347
348 return false;
349 }
350
addSubresourceAttributeURLs(ListHashSet<KURL> & urls) const351 void HTMLObjectElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const
352 {
353 HTMLPlugInImageElement::addSubresourceAttributeURLs(urls);
354
355 addSubresourceURL(urls, document()->completeURL(getAttribute(dataAttr)));
356
357 // FIXME: Passing a string that starts with "#" to the completeURL function does
358 // not seem like it would work. The image element has similar but not identical code.
359 const AtomicString& useMap = getAttribute(usemapAttr);
360 if (useMap.startsWith("#"))
361 addSubresourceURL(urls, document()->completeURL(useMap));
362 }
363
364 }
365