WebCore:
[WebKit-https.git] / WebCore / rendering / RenderPartObject.cpp
1 /**
2  * This file is part of the KDE project.
3  *
4  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5  *           (C) 2000 Simon Hausmann <hausmann@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., 51 Franklin Street, Fifth Floor,
22  * Boston, MA 02110-1301, USA.
23  *
24  */
25 #include "config.h"
26 #include "RenderPartObject.h"
27
28 #include "Document.h"
29 #include "EventHandler.h"
30 #include "Frame.h"
31 #include "FrameLoader.h"
32 #include "FrameLoaderClient.h"
33 #include "FrameTree.h"
34 #include "FrameView.h"
35 #include "HTMLEmbedElement.h"
36 #include "HTMLIFrameElement.h"
37 #include "HTMLNames.h"
38 #include "HTMLObjectElement.h"
39 #include "HTMLParamElement.h"
40 #include "KURL.h"
41 #include "MIMETypeRegistry.h"
42 #include "Page.h"
43 #include "RenderView.h"
44 #include "Text.h"
45
46 namespace WebCore {
47
48 using namespace HTMLNames;
49
50 RenderPartObject::RenderPartObject(HTMLFrameOwnerElement* element)
51     : RenderPart(element)
52 {
53     // init RenderObject attributes
54     setInline(true);
55     m_hasFallbackContent = false;
56 }
57
58 RenderPartObject::~RenderPartObject()
59 {
60     if (m_view)
61         m_view->removeWidgetToUpdate(this);
62 }
63
64 static bool isURLAllowed(Document *doc, const String &url)
65 {
66     KURL newURL(doc->completeURL(url.deprecatedString()));
67     newURL.setRef(DeprecatedString::null);
68     
69     if (doc->frame()->page()->frameCount() >= 200)
70         return false;
71
72     // We allow one level of self-reference because some sites depend on that.
73     // But we don't allow more than one.
74     bool foundSelfReference = false;
75     for (Frame *frame = doc->frame(); frame; frame = frame->tree()->parent()) {
76         KURL frameURL = frame->loader()->url();
77         frameURL.setRef(DeprecatedString::null);
78         if (frameURL == newURL) {
79             if (foundSelfReference)
80                 return false;
81             foundSelfReference = true;
82         }
83     }
84     return true;
85 }
86
87 static inline void mapClassIdToServiceType(const String& classId, String& serviceType)
88 {
89     // It is ActiveX, but the nsplugin system handling
90     // should also work, that's why we don't override the
91     // serviceType with application/x-activex-handler
92     // but let the KTrader in khtmlpart::createPart() detect
93     // the user's preference: launch with activex viewer or
94     // with nspluginviewer (Niko)
95     if (classId.contains("D27CDB6E-AE6D-11cf-96B8-444553540000"))
96         serviceType = "application/x-shockwave-flash";
97     else if (classId.contains("CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA"))
98         serviceType = "audio/x-pn-realaudio-plugin";
99     else if (classId.contains("02BF25D5-8C17-4B23-BC80-D3488ABDDC6B"))
100         serviceType = "video/quicktime";
101     else if (classId.contains("166B1BCA-3F9C-11CF-8075-444553540000"))
102         serviceType = "application/x-director";
103     else if (classId.contains("6BF52A52-394A-11d3-B153-00C04F79FAA6"))
104         serviceType = "application/x-mplayer2";
105     else if (!classId.isEmpty())
106         // We have a clsid, means this is activex (Niko)
107         serviceType = "application/x-activex-handler";
108     // TODO: add more plugins here
109 }
110
111 void RenderPartObject::updateWidget(bool onlyCreateNonNetscapePlugins)
112 {
113   String url;
114   String serviceType;
115   Vector<String> paramNames;
116   Vector<String> paramValues;
117   Frame* frame = m_view->frame();
118   
119   if (element()->hasTagName(objectTag)) {
120
121       HTMLObjectElement* o = static_cast<HTMLObjectElement*>(element());
122
123       o->setNeedWidgetUpdate(false);
124       if (!o->isComplete())
125         return;
126       // Check for a child EMBED tag.
127       HTMLEmbedElement* embed = 0;
128       for (Node* child = o->firstChild(); child;) {
129           if (child->hasTagName(embedTag)) {
130               embed = static_cast<HTMLEmbedElement*>(child);
131               break;
132           } else if (child->hasTagName(objectTag))
133               child = child->nextSibling();         // Don't descend into nested OBJECT tags
134           else
135               child = child->traverseNextNode(o);   // Otherwise descend (EMBEDs may be inside COMMENT tags)
136       }
137       
138       // Use the attributes from the EMBED tag instead of the OBJECT tag including WIDTH and HEIGHT.
139       HTMLElement *embedOrObject;
140       if (embed) {
141           embedOrObject = (HTMLElement *)embed;
142           url = embed->url;
143           serviceType = embed->m_serviceType;
144       } else
145           embedOrObject = (HTMLElement *)o;
146       
147       // If there was no URL or type defined in EMBED, try the OBJECT tag.
148       if (url.isEmpty())
149           url = o->m_url;
150       if (serviceType.isEmpty())
151           serviceType = o->m_serviceType;
152       
153       HashSet<StringImpl*, CaseInsensitiveHash<StringImpl*> > uniqueParamNames;
154       
155       // Scan the PARAM children.
156       // Get the URL and type from the params if we don't already have them.
157       // Get the attributes from the params if there is no EMBED tag.
158       Node *child = o->firstChild();
159       while (child && (url.isEmpty() || serviceType.isEmpty() || !embed)) {
160           if (child->hasTagName(paramTag)) {
161               HTMLParamElement* p = static_cast<HTMLParamElement*>(child);
162               String name = p->name().lower();
163               if (url.isEmpty() && (name == "src" || name == "movie" || name == "code" || name == "url"))
164                   url = p->value();
165               if (serviceType.isEmpty() && name == "type") {
166                   serviceType = p->value();
167                   int pos = serviceType.find(";");
168                   if (pos != -1)
169                       serviceType = serviceType.left(pos);
170               }
171               if (!embed && !name.isEmpty()) {
172                   uniqueParamNames.add(p->name().impl());
173                   paramNames.append(p->name());
174                   paramValues.append(p->value());
175               }
176           }
177           child = child->nextSibling();
178       }
179       
180       // When OBJECT is used for an applet via Sun's Java plugin, the CODEBASE attribute in the tag
181       // points to the Java plugin itself (an ActiveX component) while the actual applet CODEBASE is
182       // in a PARAM tag. See <http://java.sun.com/products/plugin/1.2/docs/tags.html>. This means
183       // we have to explicitly suppress the tag's CODEBASE attribute if there is none in a PARAM,
184       // else our Java plugin will misinterpret it. [4004531]
185       String codebase;
186       if (!embed && MIMETypeRegistry::isJavaAppletMIMEType(serviceType)) {
187           codebase = "codebase";
188           uniqueParamNames.add(codebase.impl()); // pretend we found it in a PARAM already
189       }
190       
191       // Turn the attributes of either the EMBED tag or OBJECT tag into arrays, but don't override PARAM values.
192       NamedAttrMap* attributes = embedOrObject->attributes();
193       if (attributes) {
194           for (unsigned i = 0; i < attributes->length(); ++i) {
195               Attribute* it = attributes->attributeItem(i);
196               const AtomicString& name = it->name().localName();
197               if (embed || !uniqueParamNames.contains(name.impl())) {
198                   paramNames.append(name.domString());
199                   paramValues.append(it->value().domString());
200               }
201           }
202       }
203       
204       // If we still don't have a type, try to map from a specific CLASSID to a type.
205       if (serviceType.isEmpty() && !o->m_classId.isEmpty())
206           mapClassIdToServiceType(o->m_classId, serviceType);
207       
208       if (!isURLAllowed(document(), url))
209           return;
210
211       // Find out if we support fallback content.
212       m_hasFallbackContent = false;
213       for (Node *child = o->firstChild(); child && !m_hasFallbackContent; child = child->nextSibling()) {
214           if ((!child->isTextNode() && !child->hasTagName(embedTag) && !child->hasTagName(paramTag)) || // Discount <embed> and <param>
215               (child->isTextNode() && !static_cast<Text*>(child)->containsOnlyWhitespace()))
216               m_hasFallbackContent = true;
217       }
218       
219       if (onlyCreateNonNetscapePlugins) {
220           KURL completedURL;
221           if (!url.isEmpty())
222               completedURL = frame->loader()->completeURL(url);
223         
224           if (frame->loader()->client()->objectContentType(completedURL, serviceType) == ObjectContentNetscapePlugin)
225               return;
226       }
227       
228       bool success = frame->loader()->requestObject(this, url, AtomicString(o->name()), serviceType, paramNames, paramValues);
229       if (!success && m_hasFallbackContent)
230           o->renderFallbackContent();
231   } else if (element()->hasTagName(embedTag)) {
232       HTMLEmbedElement *o = static_cast<HTMLEmbedElement*>(element());
233       o->setNeedWidgetUpdate(false);
234       url = o->url;
235       serviceType = o->m_serviceType;
236
237       if (url.isEmpty() && serviceType.isEmpty())
238           return;
239       if (!isURLAllowed(document(), url))
240           return;
241       
242       // add all attributes set on the embed object
243       NamedAttrMap* a = o->attributes();
244       if (a) {
245           for (unsigned i = 0; i < a->length(); ++i) {
246               Attribute* it = a->attributeItem(i);
247               paramNames.append(it->name().localName().domString());
248               paramValues.append(it->value().domString());
249           }
250       }
251       
252       if (onlyCreateNonNetscapePlugins) {
253           KURL completedURL;
254           if (!url.isEmpty())
255               completedURL = frame->loader()->completeURL(url);
256           
257           if (frame->loader()->client()->objectContentType(completedURL, serviceType) == ObjectContentNetscapePlugin)
258               return;
259           
260       }
261       
262       frame->loader()->requestObject(this, url, o->getAttribute(nameAttr), serviceType, paramNames, paramValues);
263   }
264 }
265
266 void RenderPartObject::layout()
267 {
268     ASSERT(needsLayout());
269
270     calcWidth();
271     calcHeight();
272     adjustOverflowForBoxShadow();
273
274     RenderPart::layout();
275
276     if (!m_widget && m_view)
277         m_view->addWidgetToUpdate(this);
278     
279     setNeedsLayout(false);
280 }
281
282 void RenderPartObject::viewCleared()
283 {
284     if (element() && m_widget && m_widget->isFrameView()) {
285         FrameView* view = static_cast<FrameView*>(m_widget);
286         int marginw = -1;
287         int marginh = -1;
288         if (element()->hasTagName(iframeTag)) {
289             HTMLIFrameElement* frame = static_cast<HTMLIFrameElement*>(element());
290             marginw = frame->getMarginWidth();
291             marginh = frame->getMarginHeight();
292         }
293         if (marginw != -1)
294             view->setMarginWidth(marginw);
295         if (marginh != -1)
296             view->setMarginHeight(marginh);
297     }
298 }
299
300 }