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, 2009, 2011 Apple Inc. All rights reserved. 6 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) 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 "core/html/HTMLObjectElement.h" 26 27#include "bindings/core/v8/ScriptEventListener.h" 28#include "core/HTMLNames.h" 29#include "core/dom/Attribute.h" 30#include "core/dom/ElementTraversal.h" 31#include "core/dom/TagCollection.h" 32#include "core/dom/Text.h" 33#include "core/dom/shadow/ShadowRoot.h" 34#include "core/fetch/ImageResource.h" 35#include "core/html/FormDataList.h" 36#include "core/html/HTMLDocument.h" 37#include "core/html/HTMLImageLoader.h" 38#include "core/html/HTMLMetaElement.h" 39#include "core/html/HTMLParamElement.h" 40#include "core/html/parser/HTMLParserIdioms.h" 41#include "core/frame/Settings.h" 42#include "core/plugins/PluginView.h" 43#include "core/rendering/RenderEmbeddedObject.h" 44#include "platform/MIMETypeRegistry.h" 45#include "platform/Widget.h" 46 47namespace blink { 48 49using namespace HTMLNames; 50 51inline HTMLObjectElement::HTMLObjectElement(Document& document, HTMLFormElement* form, bool createdByParser) 52 : HTMLPlugInElement(objectTag, document, createdByParser, ShouldNotPreferPlugInsForImages) 53 , m_useFallbackContent(false) 54{ 55 associateByParser(form); 56} 57 58inline HTMLObjectElement::~HTMLObjectElement() 59{ 60#if !ENABLE(OILPAN) 61 setForm(0); 62#endif 63} 64 65PassRefPtrWillBeRawPtr<HTMLObjectElement> HTMLObjectElement::create(Document& document, HTMLFormElement* form, bool createdByParser) 66{ 67 RefPtrWillBeRawPtr<HTMLObjectElement> element = adoptRefWillBeNoop(new HTMLObjectElement(document, form, createdByParser)); 68 element->ensureUserAgentShadowRoot(); 69 return element.release(); 70} 71 72void HTMLObjectElement::trace(Visitor* visitor) 73{ 74 FormAssociatedElement::trace(visitor); 75 HTMLPlugInElement::trace(visitor); 76} 77 78RenderWidget* HTMLObjectElement::existingRenderWidget() const 79{ 80 return renderPart(); // This will return 0 if the renderer is not a RenderPart. 81} 82 83bool HTMLObjectElement::isPresentationAttribute(const QualifiedName& name) const 84{ 85 if (name == borderAttr) 86 return true; 87 return HTMLPlugInElement::isPresentationAttribute(name); 88} 89 90void HTMLObjectElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style) 91{ 92 if (name == borderAttr) 93 applyBorderAttributeToStyle(value, style); 94 else 95 HTMLPlugInElement::collectStyleForPresentationAttribute(name, value, style); 96} 97 98void HTMLObjectElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 99{ 100 if (name == formAttr) 101 formAttributeChanged(); 102 else if (name == typeAttr) { 103 m_serviceType = value.lower(); 104 size_t pos = m_serviceType.find(";"); 105 if (pos != kNotFound) 106 m_serviceType = m_serviceType.left(pos); 107 // FIXME: What is the right thing to do here? Should we supress the 108 // reload stuff when a persistable widget-type is specified? 109 reloadPluginOnAttributeChange(name); 110 if (!renderer()) 111 requestPluginCreationWithoutRendererIfPossible(); 112 } else if (name == dataAttr) { 113 m_url = stripLeadingAndTrailingHTMLSpaces(value); 114 if (renderer() && isImageType()) { 115 setNeedsWidgetUpdate(true); 116 if (!m_imageLoader) 117 m_imageLoader = HTMLImageLoader::create(this); 118 m_imageLoader->updateFromElement(ImageLoader::UpdateIgnorePreviousError); 119 } else { 120 reloadPluginOnAttributeChange(name); 121 } 122 } else if (name == classidAttr) { 123 m_classId = value; 124 reloadPluginOnAttributeChange(name); 125 } else { 126 HTMLPlugInElement::parseAttribute(name, value); 127 } 128} 129 130static void mapDataParamToSrc(Vector<String>* paramNames, Vector<String>* paramValues) 131{ 132 // Some plugins don't understand the "data" attribute of the OBJECT tag (i.e. Real and WMP 133 // require "src" attribute). 134 int srcIndex = -1, dataIndex = -1; 135 for (unsigned i = 0; i < paramNames->size(); ++i) { 136 if (equalIgnoringCase((*paramNames)[i], "src")) 137 srcIndex = i; 138 else if (equalIgnoringCase((*paramNames)[i], "data")) 139 dataIndex = i; 140 } 141 142 if (srcIndex == -1 && dataIndex != -1) { 143 paramNames->append("src"); 144 paramValues->append((*paramValues)[dataIndex]); 145 } 146} 147 148// FIXME: This function should not deal with url or serviceType! 149void HTMLObjectElement::parametersForPlugin(Vector<String>& paramNames, Vector<String>& paramValues, String& url, String& serviceType) 150{ 151 HashSet<StringImpl*, CaseFoldingHash> uniqueParamNames; 152 String urlParameter; 153 154 // Scan the PARAM children and store their name/value pairs. 155 // Get the URL and type from the params if we don't already have them. 156 for (HTMLParamElement* p = Traversal<HTMLParamElement>::firstChild(*this); p; p = Traversal<HTMLParamElement>::nextSibling(*p)) { 157 String name = p->name(); 158 if (name.isEmpty()) 159 continue; 160 161 uniqueParamNames.add(name.impl()); 162 paramNames.append(p->name()); 163 paramValues.append(p->value()); 164 165 // FIXME: url adjustment does not belong in this function. 166 if (url.isEmpty() && urlParameter.isEmpty() && (equalIgnoringCase(name, "src") || equalIgnoringCase(name, "movie") || equalIgnoringCase(name, "code") || equalIgnoringCase(name, "url"))) 167 urlParameter = stripLeadingAndTrailingHTMLSpaces(p->value()); 168 // FIXME: serviceType calculation does not belong in this function. 169 if (serviceType.isEmpty() && equalIgnoringCase(name, "type")) { 170 serviceType = p->value(); 171 size_t pos = serviceType.find(";"); 172 if (pos != kNotFound) 173 serviceType = serviceType.left(pos); 174 } 175 } 176 177 // When OBJECT is used for an applet via Sun's Java plugin, the CODEBASE attribute in the tag 178 // points to the Java plugin itself (an ActiveX component) while the actual applet CODEBASE is 179 // in a PARAM tag. See <http://java.sun.com/products/plugin/1.2/docs/tags.html>. This means 180 // we have to explicitly suppress the tag's CODEBASE attribute if there is none in a PARAM, 181 // else our Java plugin will misinterpret it. [4004531] 182 String codebase; 183 if (MIMETypeRegistry::isJavaAppletMIMEType(serviceType)) { 184 codebase = "codebase"; 185 uniqueParamNames.add(codebase.impl()); // pretend we found it in a PARAM already 186 } 187 188 // Turn the attributes of the <object> element into arrays, but don't override <param> values. 189 AttributeCollection attributes = this->attributes(); 190 AttributeCollection::iterator end = attributes.end(); 191 for (AttributeCollection::iterator it = attributes.begin(); it != end; ++it) { 192 const AtomicString& name = it->name().localName(); 193 if (!uniqueParamNames.contains(name.impl())) { 194 paramNames.append(name.string()); 195 paramValues.append(it->value().string()); 196 } 197 } 198 199 mapDataParamToSrc(¶mNames, ¶mValues); 200 201 // HTML5 says that an object resource's URL is specified by the object's data 202 // attribute, not by a param element. However, for compatibility, allow the 203 // resource's URL to be given by a param named "src", "movie", "code" or "url" 204 // if we know that resource points to a plug-in. 205 if (url.isEmpty() && !urlParameter.isEmpty()) { 206 KURL completedURL = document().completeURL(urlParameter); 207 bool useFallback; 208 if (shouldUsePlugin(completedURL, serviceType, false, useFallback)) 209 url = urlParameter; 210 } 211} 212 213 214bool HTMLObjectElement::hasFallbackContent() const 215{ 216 for (Node* child = firstChild(); child; child = child->nextSibling()) { 217 // Ignore whitespace-only text, and <param> tags, any other content is fallback content. 218 if (child->isTextNode()) { 219 if (!toText(child)->containsOnlyWhitespace()) 220 return true; 221 } else if (!isHTMLParamElement(*child)) { 222 return true; 223 } 224 } 225 return false; 226} 227 228bool HTMLObjectElement::hasValidClassId() 229{ 230 if (MIMETypeRegistry::isJavaAppletMIMEType(m_serviceType) && classId().startsWith("java:", false)) 231 return true; 232 233 // HTML5 says that fallback content should be rendered if a non-empty 234 // classid is specified for which the UA can't find a suitable plug-in. 235 return classId().isEmpty(); 236} 237 238void HTMLObjectElement::reloadPluginOnAttributeChange(const QualifiedName& name) 239{ 240 // Following, 241 // http://www.whatwg.org/specs/web-apps/current-work/#the-object-element 242 // (Enumerated list below "Whenever one of the following conditions occur:") 243 // 244 // the updating of certain attributes should bring about "redetermination" 245 // of what the element contains. 246 bool needsInvalidation; 247 if (name == typeAttr) { 248 needsInvalidation = !fastHasAttribute(classidAttr) && !fastHasAttribute(dataAttr); 249 } else if (name == dataAttr) { 250 needsInvalidation = !fastHasAttribute(classidAttr); 251 } else if (name == classidAttr) { 252 needsInvalidation = true; 253 } else { 254 ASSERT_NOT_REACHED(); 255 needsInvalidation = false; 256 } 257 setNeedsWidgetUpdate(true); 258 if (needsInvalidation) 259 setNeedsStyleRecalc(SubtreeStyleChange); 260} 261 262// FIXME: This should be unified with HTMLEmbedElement::updateWidget and 263// moved down into HTMLPluginElement.cpp 264void HTMLObjectElement::updateWidgetInternal() 265{ 266 ASSERT(!renderEmbeddedObject()->showsUnavailablePluginIndicator()); 267 ASSERT(needsWidgetUpdate()); 268 setNeedsWidgetUpdate(false); 269 // FIXME: This should ASSERT isFinishedParsingChildren() instead. 270 if (!isFinishedParsingChildren()) { 271 dispatchErrorEvent(); 272 return; 273 } 274 275 // FIXME: I'm not sure it's ever possible to get into updateWidget during a 276 // removal, but just in case we should avoid loading the frame to prevent 277 // security bugs. 278 if (!SubframeLoadingDisabler::canLoadFrame(*this)) { 279 dispatchErrorEvent(); 280 return; 281 } 282 283 String url = this->url(); 284 String serviceType = m_serviceType; 285 286 // FIXME: These should be joined into a PluginParameters class. 287 Vector<String> paramNames; 288 Vector<String> paramValues; 289 parametersForPlugin(paramNames, paramValues, url, serviceType); 290 291 // Note: url is modified above by parametersForPlugin. 292 if (!allowedToLoadFrameURL(url)) { 293 dispatchErrorEvent(); 294 return; 295 } 296 297 // FIXME: Is it possible to get here without a renderer now that we don't have beforeload events? 298 if (!renderer()) 299 return; 300 301 if (!hasValidClassId() || !requestObject(url, serviceType, paramNames, paramValues)) { 302 if (!url.isEmpty()) 303 dispatchErrorEvent(); 304 if (hasFallbackContent()) 305 renderFallbackContent(); 306 } 307} 308 309bool HTMLObjectElement::rendererIsNeeded(const RenderStyle& style) 310{ 311 // FIXME: This check should not be needed, detached documents never render! 312 if (!document().frame()) 313 return false; 314 return HTMLPlugInElement::rendererIsNeeded(style); 315} 316 317Node::InsertionNotificationRequest HTMLObjectElement::insertedInto(ContainerNode* insertionPoint) 318{ 319 HTMLPlugInElement::insertedInto(insertionPoint); 320 FormAssociatedElement::insertedInto(insertionPoint); 321 return InsertionDone; 322} 323 324void HTMLObjectElement::removedFrom(ContainerNode* insertionPoint) 325{ 326 HTMLPlugInElement::removedFrom(insertionPoint); 327 FormAssociatedElement::removedFrom(insertionPoint); 328} 329 330void HTMLObjectElement::childrenChanged(const ChildrenChange& change) 331{ 332 if (inDocument() && !useFallbackContent()) { 333 setNeedsWidgetUpdate(true); 334 setNeedsStyleRecalc(SubtreeStyleChange); 335 } 336 HTMLPlugInElement::childrenChanged(change); 337} 338 339bool HTMLObjectElement::isURLAttribute(const Attribute& attribute) const 340{ 341 return attribute.name() == codebaseAttr || attribute.name() == dataAttr 342 || (attribute.name() == usemapAttr && attribute.value()[0] != '#') 343 || HTMLPlugInElement::isURLAttribute(attribute); 344} 345 346bool HTMLObjectElement::hasLegalLinkAttribute(const QualifiedName& name) const 347{ 348 return name == classidAttr || name == dataAttr || name == codebaseAttr || HTMLPlugInElement::hasLegalLinkAttribute(name); 349} 350 351const QualifiedName& HTMLObjectElement::subResourceAttributeName() const 352{ 353 return dataAttr; 354} 355 356const AtomicString HTMLObjectElement::imageSourceURL() const 357{ 358 return getAttribute(dataAttr); 359} 360 361// FIXME: Remove this hack. 362void HTMLObjectElement::reattachFallbackContent() 363{ 364 // This can happen inside of attach() in the middle of a recalcStyle so we need to 365 // reattach synchronously here. 366 if (document().inStyleRecalc()) 367 reattach(); 368 else 369 lazyReattachIfAttached(); 370} 371 372void HTMLObjectElement::renderFallbackContent() 373{ 374 if (useFallbackContent()) 375 return; 376 377 if (!inDocument()) 378 return; 379 380 // Before we give up and use fallback content, check to see if this is a MIME type issue. 381 if (m_imageLoader && m_imageLoader->image() && m_imageLoader->image()->status() != Resource::LoadError) { 382 m_serviceType = m_imageLoader->image()->response().mimeType(); 383 if (!isImageType()) { 384 // If we don't think we have an image type anymore, then clear the image from the loader. 385 m_imageLoader->setImage(0); 386 reattachFallbackContent(); 387 return; 388 } 389 } 390 391 m_useFallbackContent = true; 392 393 // FIXME: Style gets recalculated which is suboptimal. 394 reattachFallbackContent(); 395} 396 397bool HTMLObjectElement::isExposed() const 398{ 399 // http://www.whatwg.org/specs/web-apps/current-work/#exposed 400 for (HTMLObjectElement* ancestor = Traversal<HTMLObjectElement>::firstAncestor(*this); ancestor; ancestor = Traversal<HTMLObjectElement>::firstAncestor(*ancestor)) { 401 if (ancestor->isExposed()) 402 return false; 403 } 404 for (HTMLElement* element = Traversal<HTMLElement>::firstWithin(*this); element; element = Traversal<HTMLElement>::next(*element, this)) { 405 if (isHTMLObjectElement(*element) || isHTMLEmbedElement(*element)) 406 return false; 407 } 408 return true; 409} 410 411bool HTMLObjectElement::containsJavaApplet() const 412{ 413 if (MIMETypeRegistry::isJavaAppletMIMEType(getAttribute(typeAttr))) 414 return true; 415 416 for (HTMLElement* child = Traversal<HTMLElement>::firstChild(*this); child; child = Traversal<HTMLElement>::nextSibling(*child)) { 417 if (isHTMLParamElement(*child) 418 && equalIgnoringCase(child->getNameAttribute(), "type") 419 && MIMETypeRegistry::isJavaAppletMIMEType(child->getAttribute(valueAttr).string())) 420 return true; 421 if (isHTMLObjectElement(*child) && toHTMLObjectElement(*child).containsJavaApplet()) 422 return true; 423 if (isHTMLAppletElement(*child)) 424 return true; 425 } 426 427 return false; 428} 429 430void HTMLObjectElement::didMoveToNewDocument(Document& oldDocument) 431{ 432 FormAssociatedElement::didMoveToNewDocument(oldDocument); 433 HTMLPlugInElement::didMoveToNewDocument(oldDocument); 434} 435 436bool HTMLObjectElement::appendFormData(FormDataList& encoding, bool) 437{ 438 if (name().isEmpty()) 439 return false; 440 441 Widget* widget = pluginWidget(); 442 if (!widget || !widget->isPluginView()) 443 return false; 444 String value; 445 if (!toPluginView(widget)->getFormValue(value)) 446 return false; 447 encoding.appendData(name(), value); 448 return true; 449} 450 451HTMLFormElement* HTMLObjectElement::formOwner() const 452{ 453 return FormAssociatedElement::form(); 454} 455 456bool HTMLObjectElement::isInteractiveContent() const 457{ 458 return fastHasAttribute(usemapAttr); 459} 460 461bool HTMLObjectElement::useFallbackContent() const 462{ 463 return HTMLPlugInElement::useFallbackContent() || m_useFallbackContent; 464} 465 466} 467