2006-07-27 Eric Seidel <eric@eseidel.com>
[WebKit-https.git] / WebCore / html / HTMLObjectElement.cpp
1 /**
2  * This file is part of the DOM implementation for KDE.
3  *
4  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
6  *           (C) 2000 Stefan Schimanski (1Stein@gmx.de)
7  * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public License
20  * along with this library; see the file COPYING.LIB.  If not, write to
21  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24 #include "config.h"
25 #include "HTMLObjectElement.h"
26
27 #include "EventNames.h"
28 #include "ExceptionCode.h"
29 #include "Frame.h"
30 #include "HTMLFormElement.h"
31 #include "HTMLDocument.h"
32 #include "HTMLImageLoader.h"
33 #include "HTMLNames.h"
34 #include "Image.h"
35 #include "RenderImage.h"
36 #include "RenderPartObject.h"
37 #include "RenderWidget.h"
38 #include "Text.h"
39 #include "csshelper.h"
40
41 #if SVG_SUPPORT
42 #include "SVGDocument.h"
43 #endif
44
45 namespace WebCore {
46
47 using namespace EventNames;
48 using namespace HTMLNames;
49
50 HTMLObjectElement::HTMLObjectElement(Document *doc) 
51     : HTMLPlugInElement(objectTag, doc)
52     , needWidgetUpdate(false)
53     , m_useFallbackContent(false)
54     , m_imageLoader(0)
55     , m_complete(false)
56     , m_docNamedItem(true)
57 {
58 }
59
60 HTMLObjectElement::~HTMLObjectElement()
61 {
62 #if PLATFORM(MAC)
63     // m_instance should have been cleaned up in detach().
64     assert(!m_instance);
65 #endif
66     
67     delete m_imageLoader;
68 }
69
70 #if PLATFORM(MAC)
71 KJS::Bindings::Instance *HTMLObjectElement::getInstance() const
72 {
73     Frame* frame = document()->frame();
74     if (!frame)
75         return 0;
76
77     if (m_instance)
78         return m_instance.get();
79
80     if (RenderObject* r = renderer()) {
81         if (r->isWidget()) {
82             if (Widget* widget = static_cast<RenderWidget*>(r)->widget()) {
83                 // Call into the frame (and over the bridge) to pull the Bindings::Instance
84                 // from the guts of the plugin.
85                 m_instance = frame->getObjectInstanceForWidget(widget);
86                 // Applet may specified with <object> tag.
87                 if (!m_instance)
88                     m_instance = frame->getAppletInstanceForWidget(widget);
89             }
90         }
91     }
92
93     return m_instance.get();
94 }
95 #endif
96
97 HTMLFormElement* HTMLObjectElement::form() const
98 {
99     for (Node* p = parentNode(); p != 0; p = p->parentNode()) {
100         if (p->hasTagName(formTag))
101             return static_cast<HTMLFormElement*>(p);
102     }
103     
104     return 0;
105 }
106
107 void HTMLObjectElement::parseMappedAttribute(MappedAttribute *attr)
108 {
109     String val = attr->value();
110     int pos;
111     if (attr->name() == typeAttr) {
112         serviceType = val.deprecatedString().lower();
113         pos = serviceType.find(";");
114         if (pos != -1)
115           serviceType = serviceType.left(pos);
116         if (renderer())
117           needWidgetUpdate = true;
118         if (!isImageType() && m_imageLoader) {
119           delete m_imageLoader;
120           m_imageLoader = 0;
121         }
122     } else if (attr->name() == dataAttr) {
123         url = parseURL(val).deprecatedString();
124         if (renderer())
125           needWidgetUpdate = true;
126         if (renderer() && isImageType()) {
127           if (!m_imageLoader)
128               m_imageLoader = new HTMLImageLoader(this);
129           m_imageLoader->updateFromElement();
130         }
131     } else if (attr->name() == classidAttr) {
132         classId = val;
133         if (renderer())
134           needWidgetUpdate = true;
135     } else if (attr->name() == onloadAttr) {
136         setHTMLEventListener(loadEvent, attr);
137     } else if (attr->name() == onunloadAttr) {
138         setHTMLEventListener(unloadEvent, attr);
139     } else if (attr->name() == nameAttr) {
140             String newNameAttr = attr->value();
141             if (isDocNamedItem() && inDocument() && document()->isHTMLDocument()) {
142                 HTMLDocument *doc = static_cast<HTMLDocument *>(document());
143                 doc->removeNamedItem(oldNameAttr);
144                 doc->addNamedItem(newNameAttr);
145             }
146             oldNameAttr = newNameAttr;
147     } else if (attr->name() == idAttr) {
148         String newIdAttr = attr->value();
149         if (isDocNamedItem() && inDocument() && document()->isHTMLDocument()) {
150             HTMLDocument* doc = static_cast<HTMLDocument*>(document());
151             doc->removeDocExtraNamedItem(oldIdAttr);
152             doc->addDocExtraNamedItem(newIdAttr);
153         }
154         oldIdAttr = newIdAttr;
155         // also call superclass
156         HTMLPlugInElement::parseMappedAttribute(attr);
157     } else
158         HTMLPlugInElement::parseMappedAttribute(attr);
159 }
160
161 Document* HTMLObjectElement::contentDocument() const
162 {
163     // FIXME: The frame loading code should be moved out of the render tree
164     // and into the DOM.  Once that happens, this function should look more like
165     // HTMLFrameElement::contentDocument() and not depend on the renderer.
166     RenderObject* object = renderer();
167     if (object && object->isWidget()) {
168         RenderWidget* renderWidget = static_cast<RenderWidget*>(object);
169         if (renderWidget) {
170             Widget* widget = renderWidget->widget();
171             if (widget && widget->isFrameView()) {
172                 FrameView* frameView = static_cast<FrameView*>(widget);
173                 if (frameView->frame())
174                     return frameView->frame()->document();
175             }
176         }
177     }
178     return 0;
179 }
180
181 bool HTMLObjectElement::rendererIsNeeded(RenderStyle* style)
182 {
183     if (m_useFallbackContent || isImageType())
184         return HTMLPlugInElement::rendererIsNeeded(style);
185
186     // FIXME: This check is WRONG.  We should check the content before disabling, or do so at a later stage.
187     Frame* frame = document()->frame();
188     if (!frame || !frame->pluginsEnabled())
189         return false;
190     
191     return true;
192 }
193
194 RenderObject *HTMLObjectElement::createRenderer(RenderArena* arena, RenderStyle* style)
195 {
196     if (m_useFallbackContent)
197         return RenderObject::createObject(this, style);
198     if (isImageType())
199         return new (arena) RenderImage(this);
200     return new (arena) RenderPartObject(this);
201 }
202
203 void HTMLObjectElement::attach()
204 {
205     HTMLPlugInElement::attach();
206
207     if (renderer() && !m_useFallbackContent) {
208         if (isImageType()) {
209             if (!m_imageLoader)
210                 m_imageLoader = new HTMLImageLoader(this);
211             m_imageLoader->updateFromElement();
212             if (renderer()) {
213                 RenderImage* imageObj = static_cast<RenderImage*>(renderer());
214                 imageObj->setCachedImage(m_imageLoader->image());
215             }
216         } else {
217             if (needWidgetUpdate) {
218                 // Set needWidgetUpdate to false before calling updateWidget because updateWidget may cause
219                 // this method or recalcStyle (which also calls updateWidget) to be called.
220                 needWidgetUpdate = false;
221                 static_cast<RenderPartObject*>(renderer())->updateWidget();
222             } else {
223                 needWidgetUpdate = true;
224                 setChanged();
225             }
226         }
227     }
228 }
229
230 void HTMLObjectElement::closeRenderer()
231 {
232     // The parser just reached </object>.
233     setComplete(true);
234     
235     HTMLPlugInElement::closeRenderer();
236 }
237
238 void HTMLObjectElement::setComplete(bool complete)
239 {
240     if (complete != m_complete) {
241         m_complete = complete;
242         if (complete && !m_useFallbackContent) {
243             needWidgetUpdate = true;
244             if (inDocument())
245                 setChanged();
246         }
247     }
248 }
249
250 void HTMLObjectElement::detach()
251 {
252     if (attached() && renderer() && !m_useFallbackContent) {
253         // Update the widget the next time we attach (detaching destroys the plugin).
254         needWidgetUpdate = true;
255     }
256
257 #if PLATFORM(MAC)
258     m_instance = 0;
259 #endif
260     HTMLPlugInElement::detach();
261 }
262
263 void HTMLObjectElement::insertedIntoDocument()
264 {
265     if (isDocNamedItem() && document()->isHTMLDocument()) {
266         HTMLDocument *doc = static_cast<HTMLDocument *>(document());
267         doc->addNamedItem(oldNameAttr);
268         doc->addDocExtraNamedItem(oldIdAttr);
269     }
270
271     HTMLPlugInElement::insertedIntoDocument();
272 }
273
274 void HTMLObjectElement::removedFromDocument()
275 {
276     if (isDocNamedItem() && document()->isHTMLDocument()) {
277         HTMLDocument *doc = static_cast<HTMLDocument *>(document());
278         doc->removeNamedItem(oldNameAttr);
279         doc->removeDocExtraNamedItem(oldIdAttr);
280     }
281
282     HTMLPlugInElement::removedFromDocument();
283 }
284
285 void HTMLObjectElement::recalcStyle(StyleChange ch)
286 {
287     if (!m_useFallbackContent && needWidgetUpdate && renderer() && !isImageType()) {
288         detach();
289         attach();
290     }
291     HTMLPlugInElement::recalcStyle(ch);
292 }
293
294 void HTMLObjectElement::childrenChanged()
295 {
296     updateDocNamedItem();
297     if (inDocument() && !m_useFallbackContent) {
298         needWidgetUpdate = true;
299         setChanged();
300     }
301 }
302
303 bool HTMLObjectElement::isURLAttribute(Attribute *attr) const
304 {
305     return (attr->name() == dataAttr || (attr->name() == usemapAttr && attr->value().domString()[0] != '#'));
306 }
307
308 bool HTMLObjectElement::isImageType()
309 {
310     if (serviceType.isEmpty() && url.startsWith("data:")) {
311         // Extract the MIME type from the data URL.
312         int index = url.find(';');
313         if (index == -1)
314             index = url.find(',');
315         if (index != -1) {
316             int len = index - 5;
317             if (len > 0)
318                 serviceType = url.mid(5, len);
319             else
320                 serviceType = "text/plain"; // Data URLs with no MIME type are considered text/plain.
321         }
322     }
323     
324     return Image::supportsType(serviceType);
325 }
326
327 void HTMLObjectElement::renderFallbackContent()
328 {
329     if (m_useFallbackContent)
330         return;
331
332     // Mark ourselves as using the fallback content.
333     m_useFallbackContent = true;
334
335     // Now do a detach and reattach.    
336     // FIXME: Style gets recalculated which is suboptimal.
337     detach();
338     attach();
339 }
340
341 void HTMLObjectElement::updateDocNamedItem()
342 {
343     // The rule is "<object> elements with no children other than
344     // <param> elements and whitespace can be found by name in a
345     // document, and other <object> elements cannot."
346     bool wasNamedItem = m_docNamedItem;
347     bool isNamedItem = true;
348     Node* child = firstChild();
349     while (child && isNamedItem) {
350         if (child->isElementNode()) {
351             if (!static_cast<Element*>(child)->hasTagName(paramTag))
352                 isNamedItem = false;
353         } else if (child->isTextNode()) {
354             if (!static_cast<Text*>(child)->containsOnlyWhitespace())
355                 isNamedItem = false;
356         } else
357             isNamedItem = false;
358         child = child->nextSibling();
359     }
360     if (isNamedItem != wasNamedItem && document()->isHTMLDocument()) {
361         HTMLDocument* doc = static_cast<HTMLDocument*>(document());
362         if (isNamedItem) {
363             doc->addNamedItem(oldNameAttr);
364             doc->addDocExtraNamedItem(oldIdAttr);
365         } else {
366             doc->removeNamedItem(oldNameAttr);
367             doc->removeDocExtraNamedItem(oldIdAttr);
368         }
369     }
370     m_docNamedItem = isNamedItem;
371 }
372
373 String HTMLObjectElement::code() const
374 {
375     return getAttribute(codeAttr);
376 }
377
378 void HTMLObjectElement::setCode(const String& value)
379 {
380     setAttribute(codeAttr, value);
381 }
382
383 String HTMLObjectElement::archive() const
384 {
385     return getAttribute(archiveAttr);
386 }
387
388 void HTMLObjectElement::setArchive(const String& value)
389 {
390     setAttribute(archiveAttr, value);
391 }
392
393 String HTMLObjectElement::border() const
394 {
395     return getAttribute(borderAttr);
396 }
397
398 void HTMLObjectElement::setBorder(const String& value)
399 {
400     setAttribute(borderAttr, value);
401 }
402
403 String HTMLObjectElement::codeBase() const
404 {
405     return getAttribute(codebaseAttr);
406 }
407
408 void HTMLObjectElement::setCodeBase(const String& value)
409 {
410     setAttribute(codebaseAttr, value);
411 }
412
413 String HTMLObjectElement::codeType() const
414 {
415     return getAttribute(codetypeAttr);
416 }
417
418 void HTMLObjectElement::setCodeType(const String& value)
419 {
420     setAttribute(codetypeAttr, value);
421 }
422
423 String HTMLObjectElement::data() const
424 {
425     return getAttribute(dataAttr);
426 }
427
428 void HTMLObjectElement::setData(const String& value)
429 {
430     setAttribute(dataAttr, value);
431 }
432
433 bool HTMLObjectElement::declare() const
434 {
435     return !getAttribute(declareAttr).isNull();
436 }
437
438 void HTMLObjectElement::setDeclare(bool declare)
439 {
440     setAttribute(declareAttr, declare ? "" : 0);
441 }
442
443 int HTMLObjectElement::hspace() const
444 {
445     return getAttribute(hspaceAttr).toInt();
446 }
447
448 void HTMLObjectElement::setHspace(int value)
449 {
450     setAttribute(hspaceAttr, String::number(value));
451 }
452
453 String HTMLObjectElement::standby() const
454 {
455     return getAttribute(standbyAttr);
456 }
457
458 void HTMLObjectElement::setStandby(const String& value)
459 {
460     setAttribute(standbyAttr, value);
461 }
462
463 int HTMLObjectElement::tabIndex() const
464 {
465     return getAttribute(tabindexAttr).toInt();
466 }
467
468 void HTMLObjectElement::setTabIndex(int tabIndex)
469 {
470     setAttribute(tabindexAttr, String::number(tabIndex));
471 }
472
473 String HTMLObjectElement::type() const
474 {
475     return getAttribute(typeAttr);
476 }
477
478 void HTMLObjectElement::setType(const String& value)
479 {
480     setAttribute(typeAttr, value);
481 }
482
483 String HTMLObjectElement::useMap() const
484 {
485     return getAttribute(usemapAttr);
486 }
487
488 void HTMLObjectElement::setUseMap(const String& value)
489 {
490     setAttribute(usemapAttr, value);
491 }
492
493 int HTMLObjectElement::vspace() const
494 {
495     return getAttribute(vspaceAttr).toInt();
496 }
497
498 void HTMLObjectElement::setVspace(int value)
499 {
500     setAttribute(vspaceAttr, String::number(value));
501 }
502
503 #if SVG_SUPPORT
504 SVGDocument* HTMLObjectElement::getSVGDocument(ExceptionCode& ec) const
505 {
506     Document* doc = contentDocument();
507     if (doc && doc->isSVGDocument())
508         return static_cast<SVGDocument*>(doc);
509     // Spec: http://www.w3.org/TR/SVG/struct.html#InterfaceGetSVGDocument
510     ec = NOT_SUPPORTED_ERR;
511     return 0;
512 }
513 #endif
514
515 }