Reviewed by Eric Seidel.
[WebKit-https.git] / WebCore / 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 Apple Inc. All rights reserved.
6  * Copyright (C) 2007 Trolltech ASA
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 "FrameLoader.h"
32 #include "FrameLoaderClient.h"
33 #include "FrameView.h"
34 #include "HTMLDocument.h"
35 #include "HTMLFormElement.h"
36 #include "HTMLImageLoader.h"
37 #include "HTMLNames.h"
38 #include "Image.h"
39 #include "MIMETypeRegistry.h"
40 #include "RenderImage.h"
41 #include "RenderPartObject.h"
42 #include "RenderWidget.h"
43 #include "Text.h"
44
45 #if ENABLE(SVG)
46 #include "SVGDocument.h"
47 #endif
48
49 namespace WebCore {
50
51 using namespace EventNames;
52 using namespace HTMLNames;
53
54 HTMLObjectElement::HTMLObjectElement(Document* doc) 
55     : HTMLPlugInElement(objectTag, doc)
56     , m_needWidgetUpdate(false)
57     , m_useFallbackContent(false)
58     , m_imageLoader(0)
59     , m_complete(false)
60     , m_docNamedItem(true)
61 {
62 }
63
64 HTMLObjectElement::~HTMLObjectElement()
65 {
66 #if USE(JAVASCRIPTCORE_BINDINGS)
67     // m_instance should have been cleaned up in detach().
68     ASSERT(!m_instance);
69 #endif
70     
71     delete m_imageLoader;
72 }
73
74 #if USE(JAVASCRIPTCORE_BINDINGS)
75 KJS::Bindings::Instance *HTMLObjectElement::getInstance() const
76 {
77     Frame* frame = document()->frame();
78     if (!frame)
79         return 0;
80
81     if (m_instance)
82         return m_instance.get();
83
84     RenderWidget* renderWidget = (renderer() && renderer()->isWidget()) ? static_cast<RenderWidget*>(renderer()) : 0;
85     if (renderWidget && !renderWidget->widget()) {
86         document()->updateLayoutIgnorePendingStylesheets();
87         renderWidget = (renderer() && renderer()->isWidget()) ? static_cast<RenderWidget*>(renderer()) : 0;
88     }          
89     if (renderWidget && renderWidget->widget()) 
90         m_instance = frame->createScriptInstanceForWidget(renderWidget->widget());
91
92     return m_instance.get();
93 }
94 #endif
95
96 void HTMLObjectElement::parseMappedAttribute(MappedAttribute *attr)
97 {
98     String val = attr->value();
99     int pos;
100     if (attr->name() == typeAttr) {
101         m_serviceType = val.lower();
102         pos = m_serviceType.find(";");
103         if (pos != -1)
104           m_serviceType = m_serviceType.left(pos);
105         if (renderer())
106           m_needWidgetUpdate = true;
107         if (!isImageType() && m_imageLoader) {
108           delete m_imageLoader;
109           m_imageLoader = 0;
110         }
111     } else if (attr->name() == dataAttr) {
112         m_url = parseURL(val);
113         if (renderer())
114           m_needWidgetUpdate = true;
115         if (renderer() && isImageType()) {
116           if (!m_imageLoader)
117               m_imageLoader = new HTMLImageLoader(this);
118           m_imageLoader->updateFromElement();
119         }
120     } else if (attr->name() == classidAttr) {
121         m_classId = val;
122         if (renderer())
123           m_needWidgetUpdate = true;
124     } else if (attr->name() == onloadAttr) {
125         setHTMLEventListener(loadEvent, attr);
126     } else if (attr->name() == onunloadAttr) {
127         setHTMLEventListener(unloadEvent, attr);
128     } else if (attr->name() == nameAttr) {
129             String newNameAttr = attr->value();
130             if (isDocNamedItem() && inDocument() && document()->isHTMLDocument()) {
131                 HTMLDocument *doc = static_cast<HTMLDocument *>(document());
132                 doc->removeNamedItem(oldNameAttr);
133                 doc->addNamedItem(newNameAttr);
134             }
135             oldNameAttr = newNameAttr;
136     } else if (attr->name() == idAttr) {
137         String newIdAttr = attr->value();
138         if (isDocNamedItem() && inDocument() && document()->isHTMLDocument()) {
139             HTMLDocument* doc = static_cast<HTMLDocument*>(document());
140             doc->removeDocExtraNamedItem(oldIdAttr);
141             doc->addDocExtraNamedItem(newIdAttr);
142         }
143         oldIdAttr = newIdAttr;
144         // also call superclass
145         HTMLPlugInElement::parseMappedAttribute(attr);
146     } else
147         HTMLPlugInElement::parseMappedAttribute(attr);
148 }
149
150 bool HTMLObjectElement::rendererIsNeeded(RenderStyle* style)
151 {
152     if (m_useFallbackContent || isImageType())
153         return HTMLPlugInElement::rendererIsNeeded(style);
154
155     Frame* frame = document()->frame();
156     if (!frame)
157         return false;
158     
159     return true;
160 }
161
162 RenderObject *HTMLObjectElement::createRenderer(RenderArena* arena, RenderStyle* style)
163 {
164     if (m_useFallbackContent)
165         return RenderObject::createObject(this, style);
166     if (isImageType())
167         return new (arena) RenderImage(this);
168     return new (arena) RenderPartObject(this);
169 }
170
171 void HTMLObjectElement::attach()
172 {
173     HTMLPlugInElement::attach();
174
175     if (renderer() && !m_useFallbackContent) {
176         if (isImageType()) {
177             if (!m_imageLoader)
178                 m_imageLoader = new HTMLImageLoader(this);
179             m_imageLoader->updateFromElement();
180             if (renderer()) {
181                 RenderImage* imageObj = static_cast<RenderImage*>(renderer());
182                 imageObj->setCachedImage(m_imageLoader->image());
183             }
184         } else {
185             if (m_needWidgetUpdate) {
186                 // Set m_needWidgetUpdate to false before calling updateWidget because updateWidget may cause
187                 // this method or recalcStyle (which also calls updateWidget) to be called.
188                 m_needWidgetUpdate = false;
189                 static_cast<RenderPartObject*>(renderer())->updateWidgetSoon();
190             } else {
191                 m_needWidgetUpdate = true;
192                 setChanged();
193             }
194         }
195     }
196 }
197
198 void HTMLObjectElement::finishedParsing()
199 {
200     // The parser just reached </object>.
201     setComplete(true);
202     
203     HTMLPlugInElement::finishedParsing();
204 }
205
206 void HTMLObjectElement::setComplete(bool complete)
207 {
208     if (complete != m_complete) {
209         m_complete = complete;
210         if (complete && !m_useFallbackContent) {
211             m_needWidgetUpdate = true;
212             if (inDocument())
213                 setChanged();
214         }
215     }
216 }
217
218 void HTMLObjectElement::detach()
219 {
220     if (attached() && renderer() && !m_useFallbackContent) {
221         // Update the widget the next time we attach (detaching destroys the plugin).
222         m_needWidgetUpdate = true;
223     }
224
225 #if USE(JAVASCRIPTCORE_BINDINGS)
226     m_instance = 0;
227 #endif
228     HTMLPlugInElement::detach();
229 }
230
231 void HTMLObjectElement::insertedIntoDocument()
232 {
233     if (isDocNamedItem() && document()->isHTMLDocument()) {
234         HTMLDocument *doc = static_cast<HTMLDocument *>(document());
235         doc->addNamedItem(oldNameAttr);
236         doc->addDocExtraNamedItem(oldIdAttr);
237     }
238
239     HTMLPlugInElement::insertedIntoDocument();
240 }
241
242 void HTMLObjectElement::removedFromDocument()
243 {
244     if (isDocNamedItem() && document()->isHTMLDocument()) {
245         HTMLDocument *doc = static_cast<HTMLDocument *>(document());
246         doc->removeNamedItem(oldNameAttr);
247         doc->removeDocExtraNamedItem(oldIdAttr);
248     }
249
250     HTMLPlugInElement::removedFromDocument();
251 }
252
253 void HTMLObjectElement::recalcStyle(StyleChange ch)
254 {
255     if (!m_useFallbackContent && m_needWidgetUpdate && renderer() && !isImageType()) {
256         detach();
257         attach();
258     }
259     HTMLPlugInElement::recalcStyle(ch);
260 }
261
262 void HTMLObjectElement::childrenChanged()
263 {
264     updateDocNamedItem();
265     if (inDocument() && !m_useFallbackContent) {
266         m_needWidgetUpdate = true;
267         setChanged();
268     }
269 }
270
271 bool HTMLObjectElement::isURLAttribute(Attribute *attr) const
272 {
273     return (attr->name() == dataAttr || (attr->name() == usemapAttr && attr->value().domString()[0] != '#'));
274 }
275
276 bool HTMLObjectElement::isImageType()
277 {
278     if (m_serviceType.isEmpty() && m_url.startsWith("data:")) {
279         // Extract the MIME type from the data URL.
280         int index = m_url.find(';');
281         if (index == -1)
282             index = m_url.find(',');
283         if (index != -1) {
284             int len = index - 5;
285             if (len > 0)
286                 m_serviceType = m_url.substring(5, len);
287             else
288                 m_serviceType = "text/plain"; // Data URLs with no MIME type are considered text/plain.
289         }
290     }
291     if (Frame* frame = document()->frame()) {
292         KURL completedURL(frame->loader()->completeURL(m_url));
293         return frame->loader()->client()->objectContentType(completedURL, m_serviceType) == ObjectContentImage;
294     }
295
296     return Image::supportsType(m_serviceType);
297 }
298
299 void HTMLObjectElement::renderFallbackContent()
300 {
301     if (m_useFallbackContent)
302         return;
303
304     // Mark ourselves as using the fallback content.
305     m_useFallbackContent = true;
306
307     // Now do a detach and reattach.    
308     // FIXME: Style gets recalculated which is suboptimal.
309     detach();
310     attach();
311 }
312
313 void HTMLObjectElement::updateDocNamedItem()
314 {
315     // The rule is "<object> elements with no children other than
316     // <param> elements, unknown elements and whitespace can be
317     // found by name in a document, and other <object> elements cannot."
318     bool wasNamedItem = m_docNamedItem;
319     bool isNamedItem = true;
320     Node* child = firstChild();
321     while (child && isNamedItem) {
322         if (child->isElementNode()) {
323             Element* element = static_cast<Element*>(child);
324             if (HTMLElement::isRecognizedTagName(element->tagQName()) && !element->hasTagName(paramTag))
325                 isNamedItem = false;
326         } else if (child->isTextNode()) {
327             if (!static_cast<Text*>(child)->containsOnlyWhitespace())
328                 isNamedItem = false;
329         } else
330             isNamedItem = false;
331         child = child->nextSibling();
332     }
333     if (isNamedItem != wasNamedItem && document()->isHTMLDocument()) {
334         HTMLDocument* doc = static_cast<HTMLDocument*>(document());
335         if (isNamedItem) {
336             doc->addNamedItem(oldNameAttr);
337             doc->addDocExtraNamedItem(oldIdAttr);
338         } else {
339             doc->removeNamedItem(oldNameAttr);
340             doc->removeDocExtraNamedItem(oldIdAttr);
341         }
342     }
343     m_docNamedItem = isNamedItem;
344 }
345
346 String HTMLObjectElement::code() const
347 {
348     return getAttribute(codeAttr);
349 }
350
351 void HTMLObjectElement::setCode(const String& value)
352 {
353     setAttribute(codeAttr, value);
354 }
355
356 String HTMLObjectElement::archive() const
357 {
358     return getAttribute(archiveAttr);
359 }
360
361 void HTMLObjectElement::setArchive(const String& value)
362 {
363     setAttribute(archiveAttr, value);
364 }
365
366 String HTMLObjectElement::border() const
367 {
368     return getAttribute(borderAttr);
369 }
370
371 void HTMLObjectElement::setBorder(const String& value)
372 {
373     setAttribute(borderAttr, value);
374 }
375
376 String HTMLObjectElement::codeBase() const
377 {
378     return getAttribute(codebaseAttr);
379 }
380
381 void HTMLObjectElement::setCodeBase(const String& value)
382 {
383     setAttribute(codebaseAttr, value);
384 }
385
386 String HTMLObjectElement::codeType() const
387 {
388     return getAttribute(codetypeAttr);
389 }
390
391 void HTMLObjectElement::setCodeType(const String& value)
392 {
393     setAttribute(codetypeAttr, value);
394 }
395
396 String HTMLObjectElement::data() const
397 {
398     return getAttribute(dataAttr);
399 }
400
401 void HTMLObjectElement::setData(const String& value)
402 {
403     setAttribute(dataAttr, value);
404 }
405
406 bool HTMLObjectElement::declare() const
407 {
408     return !getAttribute(declareAttr).isNull();
409 }
410
411 void HTMLObjectElement::setDeclare(bool declare)
412 {
413     setAttribute(declareAttr, declare ? "" : 0);
414 }
415
416 int HTMLObjectElement::hspace() const
417 {
418     return getAttribute(hspaceAttr).toInt();
419 }
420
421 void HTMLObjectElement::setHspace(int value)
422 {
423     setAttribute(hspaceAttr, String::number(value));
424 }
425
426 String HTMLObjectElement::standby() const
427 {
428     return getAttribute(standbyAttr);
429 }
430
431 void HTMLObjectElement::setStandby(const String& value)
432 {
433     setAttribute(standbyAttr, value);
434 }
435
436 void HTMLObjectElement::setTabIndex(int tabIndex)
437 {
438     setAttribute(tabindexAttr, String::number(tabIndex));
439 }
440
441 String HTMLObjectElement::type() const
442 {
443     return getAttribute(typeAttr);
444 }
445
446 void HTMLObjectElement::setType(const String& value)
447 {
448     setAttribute(typeAttr, value);
449 }
450
451 String HTMLObjectElement::useMap() const
452 {
453     return getAttribute(usemapAttr);
454 }
455
456 void HTMLObjectElement::setUseMap(const String& value)
457 {
458     setAttribute(usemapAttr, value);
459 }
460
461 int HTMLObjectElement::vspace() const
462 {
463     return getAttribute(vspaceAttr).toInt();
464 }
465
466 void HTMLObjectElement::setVspace(int value)
467 {
468     setAttribute(vspaceAttr, String::number(value));
469 }
470
471 bool HTMLObjectElement::containsJavaApplet() const
472 {
473     if (MIMETypeRegistry::isJavaAppletMIMEType(type()))
474         return true;
475         
476     Node* child = firstChild();
477     while (child) {
478         if (child->isElementNode()) {
479             Element* e = static_cast<Element*>(child);
480             if (e->hasTagName(paramTag) &&
481                 e->getAttribute(nameAttr).domString().lower() == "type" &&
482                 MIMETypeRegistry::isJavaAppletMIMEType(e->getAttribute(valueAttr).domString()))
483                 return true;
484             else if (e->hasTagName(objectTag) && static_cast<HTMLObjectElement*>(e)->containsJavaApplet())
485                 return true;
486             else if (e->hasTagName(appletTag))
487                 return true;
488         }
489         child = child->nextSibling();
490     }
491     
492     return false;
493 }
494
495 #if ENABLE(SVG)
496 SVGDocument* HTMLObjectElement::getSVGDocument(ExceptionCode& ec) const
497 {
498     Document* doc = contentDocument();
499     if (doc && doc->isSVGDocument())
500         return static_cast<SVGDocument*>(doc);
501     // Spec: http://www.w3.org/TR/SVG/struct.html#InterfaceGetSVGDocument
502     ec = NOT_SUPPORTED_ERR;
503     return 0;
504 }
505 #endif
506
507 }