1 /* 2 * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org) 3 * (C) 2000 Simon Hausmann <hausmann (at) kde.org> 4 * (C) 2000 Stefan Schimanski (1Stein (at) gmx.de) 5 * Copyright (C) 2004, 2005, 2006, 2008, 2009, 2010 Apple Inc. All rights reserved. 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Library General Public 9 * License as published by the Free Software Foundation; either 10 * version 2 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Library General Public License for more details. 16 * 17 * You should have received a copy of the GNU Library General Public License 18 * along with this library; see the file COPYING.LIB. If not, write to 19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 * Boston, MA 02110-1301, USA. 21 * 22 */ 23 24 #include "config.h" 25 #include "RenderEmbeddedObject.h" 26 27 #include "Frame.h" 28 #include "FrameLoaderClient.h" 29 #include "HTMLEmbedElement.h" 30 #include "HTMLIFrameElement.h" 31 #include "HTMLNames.h" 32 #include "HTMLObjectElement.h" 33 #include "HTMLParamElement.h" 34 #include "MIMETypeRegistry.h" 35 #include "Page.h" 36 #include "PluginWidget.h" 37 #include "RenderView.h" 38 #include "RenderWidgetProtector.h" 39 #include "Text.h" 40 41 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 42 #include "HTMLVideoElement.h" 43 #endif 44 45 #if USE(ACCELERATED_COMPOSITING) 46 #include "PluginWidget.h" 47 #endif 48 49 namespace WebCore { 50 51 using namespace HTMLNames; 52 53 RenderEmbeddedObject::RenderEmbeddedObject(Element* element) 54 : RenderPartObject(element) 55 { 56 view()->frameView()->setIsVisuallyNonEmpty(); 57 } 58 59 RenderEmbeddedObject::~RenderEmbeddedObject() 60 { 61 if (frameView()) 62 frameView()->removeWidgetToUpdate(this); 63 } 64 65 #if USE(ACCELERATED_COMPOSITING) 66 bool RenderEmbeddedObject::requiresLayer() const 67 { 68 if (RenderPartObject::requiresLayer()) 69 return true; 70 71 return allowsAcceleratedCompositing(); 72 } 73 74 bool RenderEmbeddedObject::allowsAcceleratedCompositing() const 75 { 76 return widget() && widget()->isPluginWidget() && static_cast<PluginWidget*>(widget())->platformLayer(); 77 } 78 #endif 79 80 static bool isURLAllowed(Document* doc, const String& url) 81 { 82 if (doc->frame()->page()->frameCount() >= 200) 83 return false; 84 85 // We allow one level of self-reference because some sites depend on that. 86 // But we don't allow more than one. 87 KURL completeURL = doc->completeURL(url); 88 bool foundSelfReference = false; 89 for (Frame* frame = doc->frame(); frame; frame = frame->tree()->parent()) { 90 if (equalIgnoringFragmentIdentifier(frame->loader()->url(), completeURL)) { 91 if (foundSelfReference) 92 return false; 93 foundSelfReference = true; 94 } 95 } 96 return true; 97 } 98 99 typedef HashMap<String, String, CaseFoldingHash> ClassIdToTypeMap; 100 101 static ClassIdToTypeMap* createClassIdToTypeMap() 102 { 103 ClassIdToTypeMap* map = new ClassIdToTypeMap; 104 map->add("clsid:D27CDB6E-AE6D-11CF-96B8-444553540000", "application/x-shockwave-flash"); 105 map->add("clsid:CFCDAA03-8BE4-11CF-B84B-0020AFBBCCFA", "audio/x-pn-realaudio-plugin"); 106 map->add("clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B", "video/quicktime"); 107 map->add("clsid:166B1BCA-3F9C-11CF-8075-444553540000", "application/x-director"); 108 map->add("clsid:6BF52A52-394A-11D3-B153-00C04F79FAA6", "application/x-mplayer2"); 109 map->add("clsid:22D6F312-B0F6-11D0-94AB-0080C74C7E95", "application/x-mplayer2"); 110 return map; 111 } 112 113 static String serviceTypeForClassId(const String& classId) 114 { 115 // Return early if classId is empty (since we won't do anything below). 116 // Furthermore, if classId is null, calling get() below will crash. 117 if (classId.isEmpty()) 118 return String(); 119 120 static ClassIdToTypeMap* map = createClassIdToTypeMap(); 121 return map->get(classId); 122 } 123 124 static void mapDataParamToSrc(Vector<String>* paramNames, Vector<String>* paramValues) 125 { 126 // Some plugins don't understand the "data" attribute of the OBJECT tag (i.e. Real and WMP 127 // require "src" attribute). 128 int srcIndex = -1, dataIndex = -1; 129 for (unsigned int i = 0; i < paramNames->size(); ++i) { 130 if (equalIgnoringCase((*paramNames)[i], "src")) 131 srcIndex = i; 132 else if (equalIgnoringCase((*paramNames)[i], "data")) 133 dataIndex = i; 134 } 135 136 if (srcIndex == -1 && dataIndex != -1) { 137 paramNames->append("src"); 138 paramValues->append((*paramValues)[dataIndex]); 139 } 140 } 141 142 void RenderEmbeddedObject::updateWidget(bool onlyCreateNonNetscapePlugins) 143 { 144 String url; 145 String serviceType; 146 Vector<String> paramNames; 147 Vector<String> paramValues; 148 Frame* frame = frameView()->frame(); 149 150 // The calls to FrameLoader::requestObject within this function can result in a plug-in being initialized. 151 // This can run cause arbitrary JavaScript to run and may result in this RenderObject being detached from 152 // the render tree and destroyed, causing a crash like <rdar://problem/6954546>. By extending our lifetime 153 // artifically to ensure that we remain alive for the duration of plug-in initialization. 154 RenderWidgetProtector protector(this); 155 156 if (node()->hasTagName(objectTag)) { 157 HTMLObjectElement* objectElement = static_cast<HTMLObjectElement*>(node()); 158 159 objectElement->setNeedWidgetUpdate(false); 160 if (!objectElement->isFinishedParsingChildren()) 161 return; 162 163 // Check for a child EMBED tag. 164 HTMLEmbedElement* embed = 0; 165 for (Node* child = objectElement->firstChild(); child; ) { 166 if (child->hasTagName(embedTag)) { 167 embed = static_cast<HTMLEmbedElement*>(child); 168 break; 169 } 170 171 if (child->hasTagName(objectTag)) 172 child = child->nextSibling(); // Don't descend into nested OBJECT tags 173 else 174 child = child->traverseNextNode(objectElement); // Otherwise descend (EMBEDs may be inside COMMENT tags) 175 } 176 177 // Use the attributes from the EMBED tag instead of the OBJECT tag including WIDTH and HEIGHT. 178 HTMLElement* embedOrObject; 179 if (embed) { 180 embedOrObject = embed; 181 url = embed->url(); 182 serviceType = embed->serviceType(); 183 } else 184 embedOrObject = objectElement; 185 186 // If there was no URL or type defined in EMBED, try the OBJECT tag. 187 if (url.isEmpty()) 188 url = objectElement->url(); 189 if (serviceType.isEmpty()) 190 serviceType = objectElement->serviceType(); 191 192 HashSet<StringImpl*, CaseFoldingHash> uniqueParamNames; 193 194 // Scan the PARAM children. 195 // Get the URL and type from the params if we don't already have them. 196 // Get the attributes from the params if there is no EMBED tag. 197 Node* child = objectElement->firstChild(); 198 while (child && (url.isEmpty() || serviceType.isEmpty() || !embed)) { 199 if (child->hasTagName(paramTag)) { 200 HTMLParamElement* p = static_cast<HTMLParamElement*>(child); 201 String name = p->name(); 202 if (url.isEmpty() && (equalIgnoringCase(name, "src") || equalIgnoringCase(name, "movie") || equalIgnoringCase(name, "code") || equalIgnoringCase(name, "url"))) 203 url = p->value(); 204 if (serviceType.isEmpty() && equalIgnoringCase(name, "type")) { 205 serviceType = p->value(); 206 int pos = serviceType.find(";"); 207 if (pos != -1) 208 serviceType = serviceType.left(pos); 209 } 210 if (!embed && !name.isEmpty()) { 211 uniqueParamNames.add(name.impl()); 212 paramNames.append(p->name()); 213 paramValues.append(p->value()); 214 } 215 } 216 child = child->nextSibling(); 217 } 218 219 // When OBJECT is used for an applet via Sun's Java plugin, the CODEBASE attribute in the tag 220 // points to the Java plugin itself (an ActiveX component) while the actual applet CODEBASE is 221 // in a PARAM tag. See <http://java.sun.com/products/plugin/1.2/docs/tags.html>. This means 222 // we have to explicitly suppress the tag's CODEBASE attribute if there is none in a PARAM, 223 // else our Java plugin will misinterpret it. [4004531] 224 String codebase; 225 if (!embed && MIMETypeRegistry::isJavaAppletMIMEType(serviceType)) { 226 codebase = "codebase"; 227 uniqueParamNames.add(codebase.impl()); // pretend we found it in a PARAM already 228 } 229 230 // Turn the attributes of either the EMBED tag or OBJECT tag into arrays, but don't override PARAM values. 231 NamedNodeMap* attributes = embedOrObject->attributes(); 232 if (attributes) { 233 for (unsigned i = 0; i < attributes->length(); ++i) { 234 Attribute* it = attributes->attributeItem(i); 235 const AtomicString& name = it->name().localName(); 236 if (embed || !uniqueParamNames.contains(name.impl())) { 237 paramNames.append(name.string()); 238 paramValues.append(it->value().string()); 239 } 240 } 241 } 242 243 mapDataParamToSrc(¶mNames, ¶mValues); 244 245 // If we still don't have a type, try to map from a specific CLASSID to a type. 246 if (serviceType.isEmpty()) 247 serviceType = serviceTypeForClassId(objectElement->classId()); 248 249 if (!isURLAllowed(document(), url)) 250 return; 251 252 // Find out if we support fallback content. 253 m_hasFallbackContent = false; 254 for (Node* child = objectElement->firstChild(); child && !m_hasFallbackContent; child = child->nextSibling()) { 255 if ((!child->isTextNode() && !child->hasTagName(embedTag) && !child->hasTagName(paramTag)) // Discount <embed> and <param> 256 || (child->isTextNode() && !static_cast<Text*>(child)->containsOnlyWhitespace())) 257 m_hasFallbackContent = true; 258 } 259 260 if (onlyCreateNonNetscapePlugins) { 261 KURL completedURL; 262 if (!url.isEmpty()) 263 completedURL = frame->loader()->completeURL(url); 264 265 if (frame->loader()->client()->objectContentType(completedURL, serviceType) == ObjectContentNetscapePlugin) 266 return; 267 } 268 269 bool success = objectElement->dispatchBeforeLoadEvent(url) && frame->loader()->requestObject(this, url, objectElement->getAttribute(nameAttr), serviceType, paramNames, paramValues); 270 if (!success && m_hasFallbackContent) 271 objectElement->renderFallbackContent(); 272 273 } else if (node()->hasTagName(embedTag)) { 274 HTMLEmbedElement* embedElement = static_cast<HTMLEmbedElement*>(node()); 275 embedElement->setNeedWidgetUpdate(false); 276 url = embedElement->url(); 277 serviceType = embedElement->serviceType(); 278 279 if (url.isEmpty() && serviceType.isEmpty()) 280 return; 281 if (!isURLAllowed(document(), url)) 282 return; 283 284 // add all attributes set on the embed object 285 NamedNodeMap* attributes = embedElement->attributes(); 286 if (attributes) { 287 for (unsigned i = 0; i < attributes->length(); ++i) { 288 Attribute* it = attributes->attributeItem(i); 289 paramNames.append(it->name().localName().string()); 290 paramValues.append(it->value().string()); 291 } 292 } 293 294 if (onlyCreateNonNetscapePlugins) { 295 KURL completedURL; 296 if (!url.isEmpty()) 297 completedURL = frame->loader()->completeURL(url); 298 299 if (frame->loader()->client()->objectContentType(completedURL, serviceType) == ObjectContentNetscapePlugin) 300 return; 301 } 302 303 if (embedElement->dispatchBeforeLoadEvent(url)) 304 frame->loader()->requestObject(this, url, embedElement->getAttribute(nameAttr), serviceType, paramNames, paramValues); 305 } 306 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 307 else if (node()->hasTagName(videoTag) || node()->hasTagName(audioTag)) { 308 HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(node()); 309 310 mediaElement->setNeedWidgetUpdate(false); 311 if (node()->hasTagName(videoTag)) { 312 HTMLVideoElement* vid = static_cast<HTMLVideoElement*>(node()); 313 String poster = vid->poster(); 314 if (!poster.isEmpty()) { 315 paramNames.append("_media_element_poster_"); 316 paramValues.append(poster); 317 } 318 } 319 320 url = mediaElement->initialURL(); 321 if (!url.isEmpty()) { 322 paramNames.append("_media_element_src_"); 323 paramValues.append(url); 324 } 325 326 serviceType = "application/x-media-element-proxy-plugin"; 327 328 if (mediaElement->dispatchBeforeLoadEvent(url)) 329 frame->loader()->requestObject(this, url, nullAtom, serviceType, paramNames, paramValues); 330 } 331 #endif 332 } 333 334 void RenderEmbeddedObject::layout() 335 { 336 ASSERT(needsLayout()); 337 338 calcWidth(); 339 calcHeight(); 340 341 RenderPart::layout(); 342 343 m_overflow.clear(); 344 addShadowOverflow(); 345 346 if (!widget() && frameView()) 347 frameView()->addWidgetToUpdate(this); 348 349 setNeedsLayout(false); 350 } 351 352 } 353