Reviewed by Eric Seidel.
[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       if (!o->isComplete())
124         return;
125       // Check for a child EMBED tag.
126       HTMLEmbedElement* embed = 0;
127       for (Node* child = o->firstChild(); child;) {
128           if (child->hasTagName(embedTag)) {
129               embed = static_cast<HTMLEmbedElement*>(child);
130               break;
131           } else if (child->hasTagName(objectTag))
132               child = child->nextSibling();         // Don't descend into nested OBJECT tags
133           else
134               child = child->traverseNextNode(o);   // Otherwise descend (EMBEDs may be inside COMMENT tags)
135       }
136       
137       // Use the attributes from the EMBED tag instead of the OBJECT tag including WIDTH and HEIGHT.
138       HTMLElement *embedOrObject;
139       if (embed) {
140           embedOrObject = (HTMLElement *)embed;
141           url = embed->url;
142           serviceType = embed->m_serviceType;
143       } else
144           embedOrObject = (HTMLElement *)o;
145       
146       // If there was no URL or type defined in EMBED, try the OBJECT tag.
147       if (url.isEmpty())
148           url = o->m_url;
149       if (serviceType.isEmpty())
150           serviceType = o->m_serviceType;
151       
152       HashSet<StringImpl*, CaseInsensitiveHash<StringImpl*> > uniqueParamNames;
153       
154       // Scan the PARAM children.
155       // Get the URL and type from the params if we don't already have them.
156       // Get the attributes from the params if there is no EMBED tag.
157       Node *child = o->firstChild();
158       while (child && (url.isEmpty() || serviceType.isEmpty() || !embed)) {
159           if (child->hasTagName(paramTag)) {
160               HTMLParamElement* p = static_cast<HTMLParamElement*>(child);
161               String name = p->name().lower();
162               if (url.isEmpty() && (name == "src" || name == "movie" || name == "code" || name == "url"))
163                   url = p->value();
164               if (serviceType.isEmpty() && name == "type") {
165                   serviceType = p->value();
166                   int pos = serviceType.find(";");
167                   if (pos != -1)
168                       serviceType = serviceType.left(pos);
169               }
170               if (!embed && !name.isEmpty()) {
171                   uniqueParamNames.add(p->name().impl());
172                   paramNames.append(p->name());
173                   paramValues.append(p->value());
174               }
175           }
176           child = child->nextSibling();
177       }
178       
179       // When OBJECT is used for an applet via Sun's Java plugin, the CODEBASE attribute in the tag
180       // points to the Java plugin itself (an ActiveX component) while the actual applet CODEBASE is
181       // in a PARAM tag. See <http://java.sun.com/products/plugin/1.2/docs/tags.html>. This means
182       // we have to explicitly suppress the tag's CODEBASE attribute if there is none in a PARAM,
183       // else our Java plugin will misinterpret it. [4004531]
184       String codebase;
185       if (!embed && MIMETypeRegistry::isJavaAppletMIMEType(serviceType)) {
186           codebase = "codebase";
187           uniqueParamNames.add(codebase.impl()); // pretend we found it in a PARAM already
188       }
189       
190       // Turn the attributes of either the EMBED tag or OBJECT tag into arrays, but don't override PARAM values.
191       NamedAttrMap* attributes = embedOrObject->attributes();
192       if (attributes) {
193           for (unsigned i = 0; i < attributes->length(); ++i) {
194               Attribute* it = attributes->attributeItem(i);
195               const AtomicString& name = it->name().localName();
196               if (embed || !uniqueParamNames.contains(name.impl())) {
197                   paramNames.append(name.domString());
198                   paramValues.append(it->value().domString());
199               }
200           }
201       }
202       
203       // If we still don't have a type, try to map from a specific CLASSID to a type.
204       if (serviceType.isEmpty() && !o->m_classId.isEmpty())
205           mapClassIdToServiceType(o->m_classId, serviceType);
206       
207       // If no URL and type, abort.
208       if (url.isEmpty() && serviceType.isEmpty())
209           return;
210       if (!isURLAllowed(document(), url))
211           return;
212
213       // Find out if we support fallback content.
214       m_hasFallbackContent = false;
215       for (Node *child = o->firstChild(); child && !m_hasFallbackContent; child = child->nextSibling()) {
216           if ((!child->isTextNode() && !child->hasTagName(embedTag) && !child->hasTagName(paramTag)) || // Discount <embed> and <param>
217               (child->isTextNode() && !static_cast<Text*>(child)->containsOnlyWhitespace()))
218               m_hasFallbackContent = true;
219       }
220       
221       if (onlyCreateNonNetscapePlugins) {
222           KURL completedURL;
223           if (!url.isEmpty())
224               completedURL = frame->loader()->completeURL(url);
225         
226           if (frame->loader()->client()->objectContentType(completedURL, serviceType) == ObjectContentNetscapePlugin)
227               return;
228       }
229       
230       bool success = frame->loader()->requestObject(this, url, AtomicString(o->name()), serviceType, paramNames, paramValues);
231       if (!success && m_hasFallbackContent)
232           o->renderFallbackContent();
233   } else if (element()->hasTagName(embedTag)) {
234       HTMLEmbedElement *o = static_cast<HTMLEmbedElement*>(element());
235       url = o->url;
236       serviceType = o->m_serviceType;
237
238       if (url.isEmpty() && serviceType.isEmpty())
239           return;
240       if (!isURLAllowed(document(), url))
241           return;
242       
243       // add all attributes set on the embed object
244       NamedAttrMap* a = o->attributes();
245       if (a) {
246           for (unsigned i = 0; i < a->length(); ++i) {
247               Attribute* it = a->attributeItem(i);
248               paramNames.append(it->name().localName().domString());
249               paramValues.append(it->value().domString());
250           }
251       }
252       
253       if (onlyCreateNonNetscapePlugins) {
254           KURL completedURL;
255           if (!url.isEmpty())
256               completedURL = frame->loader()->completeURL(url);
257           
258           if (frame->loader()->client()->objectContentType(completedURL, serviceType) == ObjectContentNetscapePlugin)
259               return;
260           
261       }
262       
263       frame->loader()->requestObject(this, url, o->getAttribute(nameAttr), serviceType, paramNames, paramValues);
264   }
265 }
266
267 void RenderPartObject::layout()
268 {
269     ASSERT(needsLayout());
270
271     calcWidth();
272     calcHeight();
273     adjustOverflowForBoxShadow();
274
275     RenderPart::layout();
276
277     if (!m_widget && m_view)
278         m_view->addWidgetToUpdate(this);
279     
280     setNeedsLayout(false);
281 }
282
283 void RenderPartObject::updateWidgetSoon()
284 {
285     if (m_view)
286         m_view->addWidgetToUpdate(this);
287 }
288
289 void RenderPartObject::viewCleared()
290 {
291     if (element() && m_widget && m_widget->isFrameView()) {
292         FrameView* view = static_cast<FrameView*>(m_widget);
293         int marginw = -1;
294         int marginh = -1;
295         if (element()->hasTagName(iframeTag)) {
296             HTMLIFrameElement* frame = static_cast<HTMLIFrameElement*>(element());
297             marginw = frame->getMarginWidth();
298             marginh = frame->getMarginHeight();
299         }
300         if (marginw != -1)
301             view->setMarginWidth(marginw);
302         if (marginh != -1)
303             view->setMarginHeight(marginh);
304     }
305 }
306
307 }