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