WebCore:
[WebKit-https.git] / WebCore / khtml / html / html_objectimpl.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 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 "html/html_objectimpl.h"
25
26 #include "khtml_part.h"
27 #include "dom/dom_string.h"
28 #include "misc/htmlhashes.h"
29 #include "khtmlview.h"
30 #include <qstring.h>
31 #include <qmap.h>
32 #include <kdebug.h>
33
34 #include "xml/dom_docimpl.h"
35 #include "css/cssstyleselector.h"
36 #include "css/csshelper.h"
37 #include "css/cssproperties.h"
38 #include "css/cssvalues.h"
39 #include "rendering/render_applet.h"
40 #include "rendering/render_frames.h"
41 #include "rendering/render_image.h"
42 #include "xml/dom2_eventsimpl.h"
43
44 #ifndef Q_WS_QWS // We don't have Java in Qt Embedded
45 #include "java/kjavaappletwidget.h"
46 #include "java/kjavaappletcontext.h"
47 #endif
48
49 #if APPLE_CHANGES
50 #include "KWQKHTMLPart.h"
51 #endif
52
53 using namespace DOM;
54 using namespace khtml;
55
56 // -------------------------------------------------------------------------
57
58 HTMLAppletElementImpl::HTMLAppletElementImpl(DocumentPtr *doc)
59   : HTMLElementImpl(doc)
60 {
61     appletInstance = 0;
62 }
63
64 HTMLAppletElementImpl::~HTMLAppletElementImpl()
65 {
66     delete appletInstance;
67 }
68
69 NodeImpl::Id HTMLAppletElementImpl::id() const
70 {
71     return ID_APPLET;
72 }
73
74 bool HTMLAppletElementImpl::mapToEntry(NodeImpl::Id attr, MappedAttributeEntry& result) const
75 {
76     switch (attr) {
77         case ATTR_WIDTH:
78         case ATTR_HEIGHT:
79             result = eUniversal;
80             return false;
81         case ATTR_ALIGN:
82             result = eReplaced; // Share with <img> since the alignment behavior is the same.
83             return false;
84         default:
85             break;
86     }
87     
88     return HTMLElementImpl::mapToEntry(attr, result);
89 }
90
91 void HTMLAppletElementImpl::parseHTMLAttribute(HTMLAttributeImpl *attr)
92 {
93     switch (attr->id()) {
94     case ATTR_ALT:
95     case ATTR_ARCHIVE:
96     case ATTR_CODE:
97     case ATTR_CODEBASE:
98     case ATTR_MAYSCRIPT:
99     case ATTR_NAME:
100     case ATTR_OBJECT:
101         break;
102     case ATTR_WIDTH:
103         addCSSLength(attr, CSS_PROP_WIDTH, attr->value());
104         break;
105     case ATTR_HEIGHT:
106         addCSSLength(attr, CSS_PROP_HEIGHT, attr->value());
107         break;
108     case ATTR_ALIGN:
109         addHTMLAlignment(attr);
110         break;
111     default:
112         HTMLElementImpl::parseHTMLAttribute(attr);
113     }
114 }
115
116 bool HTMLAppletElementImpl::rendererIsNeeded(RenderStyle *style)
117 {
118     return !getAttribute(ATTR_CODE).isNull();
119 }
120
121 RenderObject *HTMLAppletElementImpl::createRenderer(RenderArena *arena, RenderStyle *style)
122 {
123 #ifndef Q_WS_QWS // FIXME(E)? I don't think this is possible with Qt Embedded...
124     KHTMLPart *part = getDocument()->part();
125
126     if( part && part->javaEnabled() )
127     {
128         QMap<QString, QString> args;
129
130         args.insert( "code", getAttribute(ATTR_CODE).string());
131         DOMString codeBase = getAttribute(ATTR_CODEBASE);
132         if(!codeBase.isNull())
133             args.insert( "codeBase", codeBase.string() );
134         DOMString name = getDocument()->htmlMode() != DocumentImpl::XHtml ?
135                          getAttribute(ATTR_NAME) : getAttribute(ATTR_ID);
136         if(!name.isNull())
137             args.insert( "name", name.string() );
138         DOMString archive = getAttribute(ATTR_ARCHIVE);
139         if(!archive.isNull())
140             args.insert( "archive", archive.string() );
141
142         args.insert( "baseURL", getDocument()->baseURL() );
143
144         DOMString mayScript = getAttribute(ATTR_MAYSCRIPT);
145         if (!mayScript.isNull())
146             args.insert("mayScript", mayScript.string());
147
148         // Other arguments (from <PARAM> tags) are added later.
149         
150         return new (getDocument()->renderArena()) RenderApplet(this, args);
151     }
152
153     // ### remove me. we should never show an empty applet, instead
154     // render the alternative content given by the webpage
155     return new (getDocument()->renderArena()) RenderEmptyApplet(this);
156 #else
157     return 0;
158 #endif
159 }
160
161 bool HTMLAppletElementImpl::getMember(const QString & name, JType & type, QString & val) {
162 #if APPLE_CHANGES
163     return false;
164 #else
165 #ifndef Q_WS_QWS // We don't have Java in Qt Embedded
166     if ( !m_render || !m_render->isApplet() )
167         return false;
168     KJavaAppletWidget *w = static_cast<KJavaAppletWidget*>(static_cast<RenderApplet*>(m_render)->widget());
169     return (w && w->applet() && w->applet()->getMember(name, type, val));
170 #else
171     return false;
172 #endif
173 #endif
174 }
175
176 bool HTMLAppletElementImpl::callMember(const QString & name, const QStringList & args, JType & type, QString & val) {
177 #if APPLE_CHANGES
178     return false;
179 #else
180 #ifndef Q_WS_QWS // We don't have Java in Qt Embedded
181     if ( !m_render || !m_render->isApplet() )
182         return false;
183     KJavaAppletWidget *w = static_cast<KJavaAppletWidget*>(static_cast<RenderApplet*>(m_render)->widget());
184     return (w && w->applet() && w->applet()->callMember(name, args, type, val));
185 #else
186     return false;
187 #endif
188 #endif
189 }
190
191 #if APPLE_CHANGES
192 KJS::Bindings::Instance *HTMLAppletElementImpl::getAppletInstance() const
193 {
194     KHTMLPart* part = getDocument()->part();
195     if (!part || !part->javaEnabled())
196         return 0;
197
198     if (appletInstance)
199         return appletInstance;
200     
201     RenderApplet *r = static_cast<RenderApplet*>(m_render);
202     if (r) {
203         r->createWidgetIfNecessary();
204         if (r->widget()){
205             // Call into the part (and over the bridge) to pull the Bindings::Instance
206             // from the guts of the plugin.
207             void *_view = r->widget()->getView();
208             appletInstance = KWQ(part)->getAppletInstanceForView((NSView *)_view);
209         }
210     }
211     return appletInstance;
212 }
213 #endif
214
215 // -------------------------------------------------------------------------
216
217 HTMLEmbedElementImpl::HTMLEmbedElementImpl(DocumentPtr *doc)
218     : HTMLElementImpl(doc), embedInstance(0)
219 {}
220
221 HTMLEmbedElementImpl::~HTMLEmbedElementImpl()
222 {
223 }
224
225 NodeImpl::Id HTMLEmbedElementImpl::id() const
226 {
227     return ID_EMBED;
228 }
229
230 #if APPLE_CHANGES
231 KJS::Bindings::Instance *HTMLEmbedElementImpl::getEmbedInstance() const
232 {
233     KHTMLPart* part = getDocument()->part();
234     if (!part)
235         return 0;
236
237     if (embedInstance)
238         return embedInstance;
239     
240     RenderPartObject *r = static_cast<RenderPartObject*>(m_render);
241     if (r) {
242         if (r->widget()){
243             // Call into the part (and over the bridge) to pull the Bindings::Instance
244             // from the guts of the Java VM.
245             void *_view = r->widget()->getView();
246             embedInstance = KWQ(part)->getEmbedInstanceForView((NSView *)_view);
247         }
248     }
249     return embedInstance;
250 }
251 #endif
252
253 bool HTMLEmbedElementImpl::mapToEntry(NodeImpl::Id attr, MappedAttributeEntry& result) const
254 {
255     switch (attr) {
256         case ATTR_WIDTH:
257         case ATTR_HEIGHT:
258         case ATTR_BORDER:
259         case ATTR_VSPACE:
260         case ATTR_HSPACE:
261         case ATTR_VALIGN:
262         case ATTR_HIDDEN:
263             result = eUniversal;
264             return false;
265         case ATTR_ALIGN:
266             result = eReplaced; // Share with <img> since the alignment behavior is the same.
267             return false;
268         default:
269             break;
270     }
271     
272     return HTMLElementImpl::mapToEntry(attr, result);
273 }
274
275 void HTMLEmbedElementImpl::parseHTMLAttribute(HTMLAttributeImpl *attr)
276 {
277   QString val = attr->value().string();
278   
279   int pos;
280   switch ( attr->id() )
281   {
282      case ATTR_TYPE:
283         serviceType = val.lower();
284         pos = serviceType.find( ";" );
285         if ( pos!=-1 )
286             serviceType = serviceType.left( pos );
287         break;
288      case ATTR_CODE:
289      case ATTR_SRC:
290          url = khtml::parseURL(attr->value()).string();
291          break;
292      case ATTR_WIDTH:
293         addCSSLength( attr, CSS_PROP_WIDTH, attr->value() );
294         break;
295      case ATTR_HEIGHT:
296         addCSSLength( attr, CSS_PROP_HEIGHT, attr->value());
297         break;
298      case ATTR_BORDER:
299         addCSSLength(attr, CSS_PROP_BORDER_WIDTH, attr->value());
300         addCSSProperty( attr, CSS_PROP_BORDER_TOP_STYLE, CSS_VAL_SOLID );
301         addCSSProperty( attr, CSS_PROP_BORDER_RIGHT_STYLE, CSS_VAL_SOLID );
302         addCSSProperty( attr, CSS_PROP_BORDER_BOTTOM_STYLE, CSS_VAL_SOLID );
303         addCSSProperty( attr, CSS_PROP_BORDER_LEFT_STYLE, CSS_VAL_SOLID );
304         break;
305      case ATTR_VSPACE:
306         addCSSLength(attr, CSS_PROP_MARGIN_TOP, attr->value());
307         addCSSLength(attr, CSS_PROP_MARGIN_BOTTOM, attr->value());
308         break;
309      case ATTR_HSPACE:
310         addCSSLength(attr, CSS_PROP_MARGIN_LEFT, attr->value());
311         addCSSLength(attr, CSS_PROP_MARGIN_RIGHT, attr->value());
312         break;
313      case ATTR_ALIGN:
314         addHTMLAlignment(attr);
315         break;
316      case ATTR_VALIGN:
317         addCSSProperty(attr, CSS_PROP_VERTICAL_ALIGN, attr->value());
318         break;
319      case ATTR_PLUGINPAGE:
320      case ATTR_PLUGINSPAGE:
321         pluginPage = val;
322         break;
323      case ATTR_HIDDEN:
324         if (val.lower()=="yes" || val.lower()=="true") {
325             // FIXME: Not dynamic, but it's not really important that such a rarely-used
326             // feature work dynamically.
327             addCSSLength( attr, CSS_PROP_WIDTH, "0" );
328             addCSSLength( attr, CSS_PROP_HEIGHT, "0" );
329         }
330         break;
331      default:
332         HTMLElementImpl::parseHTMLAttribute( attr );
333   }
334 }
335
336 bool HTMLEmbedElementImpl::rendererIsNeeded(RenderStyle *style)
337 {
338     KHTMLPart *part = getDocument()->part();
339     if (!part)
340         return false;
341     return part->pluginsEnabled() && parentNode()->id() != ID_OBJECT;
342 }
343
344 RenderObject *HTMLEmbedElementImpl::createRenderer(RenderArena *arena, RenderStyle *style)
345 {
346     return new (arena) RenderPartObject(this);
347 }
348
349 void HTMLEmbedElementImpl::attach()
350 {
351     HTMLElementImpl::attach();
352     if (m_render) {
353         static_cast<RenderPartObject*>(m_render)->updateWidget();
354     }
355 }
356
357 bool HTMLEmbedElementImpl::isURLAttribute(AttributeImpl *attr) const
358 {
359     return attr->id() == ATTR_SRC;
360 }
361
362 // -------------------------------------------------------------------------
363
364 HTMLObjectElementImpl::HTMLObjectElementImpl(DocumentPtr *doc) 
365 #if APPLE_CHANGES
366 : HTMLElementImpl(doc), m_imageLoader(0), objectInstance(0)
367 #else
368 : HTMLElementImpl(doc), m_imageLoader(0)
369 #endif
370 {
371     needWidgetUpdate = false;
372 }
373
374 HTMLObjectElementImpl::~HTMLObjectElementImpl()
375 {
376     delete m_imageLoader;
377 }
378
379 NodeImpl::Id HTMLObjectElementImpl::id() const
380 {
381     return ID_OBJECT;
382 }
383
384 #if APPLE_CHANGES
385 KJS::Bindings::Instance *HTMLObjectElementImpl::getObjectInstance() const
386 {
387     KHTMLPart* part = getDocument()->part();
388     if (!part)
389         return 0;
390
391     if (objectInstance)
392         return objectInstance;
393     
394     RenderPartObject *r = static_cast<RenderPartObject*>(m_render);
395     if (r) {
396         if (r->widget()){
397             // Call into the part (and over the bridge) to pull the Bindings::Instance
398             // from the guts of the plugin.
399             void *_view = r->widget()->getView();
400             objectInstance = KWQ(part)->getObjectInstanceForView((NSView *)_view);
401         }
402     }
403     return objectInstance;
404 }
405 #endif
406
407 HTMLFormElementImpl *HTMLObjectElementImpl::form() const
408 {
409   return 0;
410 }
411
412 bool HTMLObjectElementImpl::mapToEntry(NodeImpl::Id attr, MappedAttributeEntry& result) const
413 {
414     switch (attr) {
415         case ATTR_WIDTH:
416         case ATTR_HEIGHT:
417             result = eUniversal;
418             return false;
419         default:
420             break;
421     }
422     
423     return HTMLElementImpl::mapToEntry(attr, result);
424 }
425
426 void HTMLObjectElementImpl::parseHTMLAttribute(HTMLAttributeImpl *attr)
427 {
428   QString val = attr->value().string();
429   int pos;
430   switch ( attr->id() )
431   {
432     case ATTR_TYPE:
433       serviceType = val.lower();
434       pos = serviceType.find( ";" );
435       if ( pos!=-1 )
436           serviceType = serviceType.left( pos );
437       if (m_render)
438           needWidgetUpdate = true;
439       if (!canRenderImageType(serviceType) && m_imageLoader) {
440           delete m_imageLoader;
441           m_imageLoader = 0;
442       }
443       break;
444     case ATTR_DATA:
445       url = khtml::parseURL(  val ).string();
446       if (m_render)
447           needWidgetUpdate = true;
448       if (m_render && canRenderImageType(serviceType)) {
449           if (!m_imageLoader)
450               m_imageLoader = new HTMLImageLoader(this);
451           m_imageLoader->updateFromElement();
452       }
453       break;
454     case ATTR_WIDTH:
455       addCSSLength( attr, CSS_PROP_WIDTH, attr->value());
456       break;
457     case ATTR_HEIGHT:
458       addCSSLength( attr, CSS_PROP_HEIGHT, attr->value());
459       break;
460     case ATTR_CLASSID:
461       classId = val;
462       if (m_render)
463           needWidgetUpdate = true;
464       break;
465     case ATTR_ONLOAD: // ### support load/unload on object elements
466         setHTMLEventListener(EventImpl::LOAD_EVENT,
467             getDocument()->createHTMLEventListener(attr->value().string()));
468         break;
469     case ATTR_ONUNLOAD:
470         setHTMLEventListener(EventImpl::UNLOAD_EVENT,
471             getDocument()->createHTMLEventListener(attr->value().string()));
472         break;
473     default:
474       HTMLElementImpl::parseHTMLAttribute( attr );
475   }
476 }
477
478 DocumentImpl* HTMLObjectElementImpl::contentDocument() const
479 {
480     // ###
481     return 0;
482 }
483
484 bool HTMLObjectElementImpl::rendererIsNeeded(RenderStyle *style)
485 {
486     if (canRenderImageType(serviceType)) {
487         return HTMLElementImpl::rendererIsNeeded(style);
488     }
489
490     KHTMLPart* part = getDocument()->part();
491     if (!part || !part->pluginsEnabled()) {
492         return false;
493     }
494 #if APPLE_CHANGES
495     // Eventually we will merge with the better version of this check on the tip of tree.
496     // Until then, just leave it out.
497 #else
498     KURL u = getDocument()->completeURL(url);
499     for (KHTMLPart* part = w->part()->parentPart(); part; part = part->parentPart())
500         if (part->url() == u) {
501             return false;
502         }
503 #endif
504     return true;
505 }
506
507 RenderObject *HTMLObjectElementImpl::createRenderer(RenderArena *arena, RenderStyle *style)
508 {
509     if (canRenderImageType(serviceType)) {
510         return new (arena) RenderImage(this);
511     }
512     return new (arena) RenderPartObject(this);
513 }
514
515 void HTMLObjectElementImpl::attach()
516 {
517     HTMLElementImpl::attach();
518
519     if (m_render) {
520         if (canRenderImageType(serviceType)) {
521             if (!m_imageLoader)
522                 m_imageLoader = new HTMLImageLoader(this);
523             m_imageLoader->updateFromElement();
524             if (renderer()) {
525                 RenderImage* imageObj = static_cast<RenderImage*>(renderer());
526                 imageObj->setImage(m_imageLoader->image());
527             }
528         } else {
529             if (needWidgetUpdate) {
530                 static_cast<RenderPartObject*>(m_render)->updateWidget();
531                 dispatchHTMLEvent(EventImpl::LOAD_EVENT,false,false);
532                 needWidgetUpdate = false;
533             } else {
534                 needWidgetUpdate = true;
535                 setChanged();
536             }
537         }
538     }
539 }
540
541 void HTMLObjectElementImpl::detach()
542 {
543     // Only bother with an unload event if we had a render object.  - dwh
544     if (attached() && m_render)
545         // ### do this when we are actualy removed from document instead
546         dispatchHTMLEvent(EventImpl::UNLOAD_EVENT,false,false);
547
548   HTMLElementImpl::detach();
549 }
550
551 void HTMLObjectElementImpl::recalcStyle(StyleChange ch)
552 {
553     if (needWidgetUpdate && m_render && !canRenderImageType(serviceType)) {
554         static_cast<RenderPartObject*>(m_render)->updateWidget();
555         dispatchHTMLEvent(EventImpl::LOAD_EVENT,false,false);
556         needWidgetUpdate = false;
557     }
558     HTMLElementImpl::recalcStyle(ch);
559 }
560
561 void HTMLObjectElementImpl::childrenChanged()
562 {
563     if (inDocument()) {
564         needWidgetUpdate = true;
565         setChanged();
566     }
567 }
568
569 bool HTMLObjectElementImpl::isURLAttribute(AttributeImpl *attr) const
570 {
571     return (attr->id() == ATTR_DATA || (attr->id() == ATTR_USEMAP && attr->value().domString()[0] != '#'));
572 }
573
574 // -------------------------------------------------------------------------
575
576 HTMLParamElementImpl::HTMLParamElementImpl(DocumentPtr *doc)
577     : HTMLElementImpl(doc)
578 {
579 }
580
581 HTMLParamElementImpl::~HTMLParamElementImpl()
582 {
583 }
584
585 NodeImpl::Id HTMLParamElementImpl::id() const
586 {
587     return ID_PARAM;
588 }
589
590 void HTMLParamElementImpl::parseHTMLAttribute(HTMLAttributeImpl *attr)
591 {
592     switch( attr->id() )
593     {
594     case ATTR_ID:
595         // Must call base class so that hasID bit gets set.
596         HTMLElementImpl::parseHTMLAttribute(attr);
597         if (getDocument()->htmlMode() != DocumentImpl::XHtml) break;
598         // fall through
599     case ATTR_NAME:
600         m_name = attr->value();
601         break;
602     case ATTR_VALUE:
603         m_value = attr->value();
604         break;
605     }
606 }
607
608 bool HTMLParamElementImpl::isURLAttribute(AttributeImpl *attr) const
609 {
610     if (attr->id() == ATTR_VALUE) {
611         AttributeImpl *attr = attributes()->getAttributeItem(ATTR_NAME);
612         if (attr) {
613             DOMString value = attr->value().string().lower();
614             if (value == "src" || value == "movie" || value == "data") {
615                 return true;
616             }
617         }
618     }
619     return false;
620 }