1 /* 2 * Copyright (C) 2008, 2011, 2012 Apple Inc. All rights reserved. 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Library General Public 6 * License as published by the Free Software Foundation; either 7 * version 2 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Library General Public License for more details. 13 * 14 * You should have received a copy of the GNU Library General Public License 15 * along with this library; see the file COPYING.LIB. If not, write to 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 * Boston, MA 02110-1301, USA. 18 * 19 */ 20 21 #include "config.h" 22 #include "core/html/HTMLPlugInImageElement.h" 23 24 #include "bindings/v8/ScriptController.h" 25 #include "core/html/HTMLImageLoader.h" 26 #include "core/html/PluginDocument.h" 27 #include "core/loader/FrameLoader.h" 28 #include "core/loader/FrameLoaderClient.h" 29 #include "core/page/ContentSecurityPolicy.h" 30 #include "core/page/Frame.h" 31 #include "core/page/Page.h" 32 #include "core/page/Settings.h" 33 #include "core/platform/Logging.h" 34 #include "core/platform/MIMETypeFromURL.h" 35 #include "core/platform/MIMETypeRegistry.h" 36 #include "core/platform/graphics/Image.h" 37 #include "core/plugins/PluginData.h" 38 #include "core/rendering/RenderEmbeddedObject.h" 39 #include "core/rendering/RenderImage.h" 40 #include "weborigin/SecurityOrigin.h" 41 42 namespace WebCore { 43 44 using namespace HTMLNames; 45 46 typedef Vector<RefPtr<HTMLPlugInImageElement> > HTMLPlugInImageElementList; 47 48 static const int sizingTinyDimensionThreshold = 40; 49 static const int sizingSmallWidthThreshold = 250; 50 static const int sizingMediumWidthThreshold = 450; 51 static const int sizingMediumHeightThreshold = 300; 52 static const float sizingFullPageAreaRatioThreshold = 0.96; 53 static const float autostartSoonAfterUserGestureThreshold = 5.0; 54 55 HTMLPlugInImageElement::HTMLPlugInImageElement(const QualifiedName& tagName, Document* document, bool createdByParser, PreferPlugInsForImagesOption preferPlugInsForImagesOption) 56 : HTMLPlugInElement(tagName, document) 57 // m_needsWidgetUpdate(!createdByParser) allows HTMLObjectElement to delay 58 // widget updates until after all children are parsed. For HTMLEmbedElement 59 // this delay is unnecessary, but it is simpler to make both classes share 60 // the same codepath in this class. 61 , m_needsWidgetUpdate(!createdByParser) 62 , m_shouldPreferPlugInsForImages(preferPlugInsForImagesOption == ShouldPreferPlugInsForImages) 63 , m_createdDuringUserGesture(ScriptController::processingUserGesture()) 64 { 65 setHasCustomStyleCallbacks(); 66 } 67 68 HTMLPlugInImageElement::~HTMLPlugInImageElement() 69 { 70 } 71 72 void HTMLPlugInImageElement::setDisplayState(DisplayState state) 73 { 74 HTMLPlugInElement::setDisplayState(state); 75 } 76 77 RenderEmbeddedObject* HTMLPlugInImageElement::renderEmbeddedObject() const 78 { 79 // HTMLObjectElement and HTMLEmbedElement may return arbitrary renderers 80 // when using fallback content. 81 if (!renderer() || !renderer()->isEmbeddedObject()) 82 return 0; 83 return toRenderEmbeddedObject(renderer()); 84 } 85 86 bool HTMLPlugInImageElement::isImageType() 87 { 88 if (m_serviceType.isEmpty() && protocolIs(m_url, "data")) 89 m_serviceType = mimeTypeFromDataURL(m_url); 90 91 if (Frame* frame = document()->frame()) { 92 KURL completedURL = document()->completeURL(m_url); 93 return frame->loader()->client()->objectContentType(completedURL, m_serviceType, shouldPreferPlugInsForImages()) == ObjectContentImage; 94 } 95 96 return Image::supportsType(m_serviceType); 97 } 98 99 // We don't use m_url, as it may not be the final URL that the object loads, 100 // depending on <param> values. 101 bool HTMLPlugInImageElement::allowedToLoadFrameURL(const String& url) 102 { 103 KURL completeURL = document()->completeURL(url); 104 105 if (contentFrame() && protocolIsJavaScript(completeURL) 106 && !document()->securityOrigin()->canAccess(contentDocument()->securityOrigin())) 107 return false; 108 109 return document()->frame()->isURLAllowed(completeURL); 110 } 111 112 // We don't use m_url, or m_serviceType as they may not be the final values 113 // that <object> uses depending on <param> values. 114 bool HTMLPlugInImageElement::wouldLoadAsNetscapePlugin(const String& url, const String& serviceType) 115 { 116 ASSERT(document()); 117 ASSERT(document()->frame()); 118 KURL completedURL; 119 if (!url.isEmpty()) 120 completedURL = document()->completeURL(url); 121 122 FrameLoader* frameLoader = document()->frame()->loader(); 123 ASSERT(frameLoader); 124 if (frameLoader->client()->objectContentType(completedURL, serviceType, shouldPreferPlugInsForImages()) == ObjectContentNetscapePlugin) 125 return true; 126 return false; 127 } 128 129 RenderObject* HTMLPlugInImageElement::createRenderer(RenderStyle* style) 130 { 131 // Fallback content breaks the DOM->Renderer class relationship of this 132 // class and all superclasses because createObject won't necessarily 133 // return a RenderEmbeddedObject, RenderPart or even RenderWidget. 134 if (useFallbackContent()) 135 return RenderObject::createObject(this, style); 136 137 if (isImageType()) { 138 RenderImage* image = new RenderImage(this); 139 image->setImageResource(RenderImageResource::create()); 140 return image; 141 } 142 143 return new RenderEmbeddedObject(this); 144 } 145 146 void HTMLPlugInImageElement::willRecalcStyle(StyleChange) 147 { 148 // FIXME: Why is this necessary? Manual re-attach is almost always wrong. 149 if (!useFallbackContent() && needsWidgetUpdate() && renderer() && !isImageType()) 150 reattach(); 151 } 152 153 void HTMLPlugInImageElement::attach(const AttachContext& context) 154 { 155 PostAttachCallbackDisabler disabler(this); 156 157 bool isImage = isImageType(); 158 159 if (!isImage) 160 queuePostAttachCallback(&HTMLPlugInImageElement::updateWidgetCallback, this); 161 162 HTMLPlugInElement::attach(context); 163 164 if (isImage && renderer() && !useFallbackContent()) { 165 if (!m_imageLoader) 166 m_imageLoader = adoptPtr(new HTMLImageLoader(this)); 167 m_imageLoader->updateFromElement(); 168 } 169 } 170 171 void HTMLPlugInImageElement::detach(const AttachContext& context) 172 { 173 // FIXME: Because of the insanity that is HTMLPlugInImageElement::recalcStyle, 174 // we can end up detaching during an attach() call, before we even have a 175 // renderer. In that case, don't mark the widget for update. 176 if (attached() && renderer() && !useFallbackContent()) 177 // Update the widget the next time we attach (detaching destroys the plugin). 178 setNeedsWidgetUpdate(true); 179 HTMLPlugInElement::detach(context); 180 } 181 182 void HTMLPlugInImageElement::updateWidgetIfNecessary() 183 { 184 document()->updateStyleIfNeeded(); 185 186 if (!needsWidgetUpdate() || useFallbackContent() || isImageType()) 187 return; 188 189 if (!renderEmbeddedObject() || renderEmbeddedObject()->showsUnavailablePluginIndicator()) 190 return; 191 192 updateWidget(CreateOnlyNonNetscapePlugins); 193 } 194 195 void HTMLPlugInImageElement::finishParsingChildren() 196 { 197 HTMLPlugInElement::finishParsingChildren(); 198 if (useFallbackContent()) 199 return; 200 201 setNeedsWidgetUpdate(true); 202 if (inDocument()) 203 setNeedsStyleRecalc(); 204 } 205 206 void HTMLPlugInImageElement::didMoveToNewDocument(Document* oldDocument) 207 { 208 if (m_imageLoader) 209 m_imageLoader->elementDidMoveToNewDocument(); 210 HTMLPlugInElement::didMoveToNewDocument(oldDocument); 211 } 212 213 void HTMLPlugInImageElement::updateWidgetCallback(Node* n) 214 { 215 toHTMLPlugInImageElement(n)->updateWidgetIfNecessary(); 216 } 217 218 bool HTMLPlugInImageElement::requestObject(const String& url, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues) 219 { 220 if (url.isEmpty() && mimeType.isEmpty()) 221 return false; 222 223 // FIXME: None of this code should use renderers! 224 RenderEmbeddedObject* renderer = renderEmbeddedObject(); 225 ASSERT(renderer); 226 if (!renderer) 227 return false; 228 229 KURL completedURL = document()->completeURL(url); 230 231 bool useFallback; 232 if (shouldUsePlugin(completedURL, mimeType, renderer->hasFallbackContent(), useFallback)) { 233 bool success = loadPlugin(completedURL, mimeType, paramNames, paramValues, useFallback); 234 return success; 235 } 236 237 // If the plug-in element already contains a subframe, loadOrRedirectSubframe will re-use it. Otherwise, 238 // it will create a new frame and set it as the RenderPart's widget, causing what was previously 239 // in the widget to be torn down. 240 return loadOrRedirectSubframe(completedURL, getNameAttribute(), true); 241 } 242 243 bool HTMLPlugInImageElement::loadPlugin(const KURL& url, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback) 244 { 245 Frame* frame = document()->frame(); 246 247 if (!frame->loader()->allowPlugins(AboutToInstantiatePlugin)) 248 return false; 249 250 if (!pluginIsLoadable(url, mimeType)) 251 return false; 252 253 RenderEmbeddedObject* renderer = renderEmbeddedObject(); 254 255 // FIXME: This code should not depend on renderer! 256 if (!renderer || useFallback) 257 return false; 258 259 LOG(Plugins, "%p Plug-in URL: %s", this, m_url.utf8().data()); 260 LOG(Plugins, " Loaded URL: %s", url.string().utf8().data()); 261 m_loadedUrl = url; 262 263 IntSize contentSize = roundedIntSize(LayoutSize(renderer->contentWidth(), renderer->contentHeight())); 264 bool loadManually = document()->isPluginDocument() && !frame->loader()->containsPlugins() && toPluginDocument(document())->shouldLoadPluginManually(); 265 RefPtr<Widget> widget = frame->loader()->client()->createPlugin(contentSize, this, url, paramNames, paramValues, mimeType, loadManually); 266 267 if (!widget) { 268 if (!renderer->showsUnavailablePluginIndicator()) 269 renderer->setPluginUnavailabilityReason(RenderEmbeddedObject::PluginMissing); 270 return false; 271 } 272 273 renderer->setWidget(widget); 274 frame->loader()->setContainsPlugins(); 275 setNeedsStyleRecalc(LocalStyleChange, StyleChangeFromRenderer); 276 return true; 277 } 278 279 bool HTMLPlugInImageElement::shouldUsePlugin(const KURL& url, const String& mimeType, bool hasFallback, bool& useFallback) 280 { 281 // Allow other plug-ins to win over QuickTime because if the user has installed a plug-in that 282 // can handle TIFF (which QuickTime can also handle) they probably intended to override QT. 283 if (document()->frame()->page() && (mimeType == "image/tiff" || mimeType == "image/tif" || mimeType == "image/x-tiff")) { 284 const PluginData* pluginData = document()->frame()->page()->pluginData(); 285 String pluginName = pluginData ? pluginData->pluginNameForMimeType(mimeType) : String(); 286 if (!pluginName.isEmpty() && !pluginName.contains("QuickTime", false)) 287 return true; 288 } 289 290 ObjectContentType objectType = document()->frame()->loader()->client()->objectContentType(url, mimeType, shouldPreferPlugInsForImages()); 291 // If an object's content can't be handled and it has no fallback, let 292 // it be handled as a plugin to show the broken plugin icon. 293 useFallback = objectType == ObjectContentNone && hasFallback; 294 return objectType == ObjectContentNone || objectType == ObjectContentNetscapePlugin || objectType == ObjectContentOtherPlugin; 295 296 } 297 298 bool HTMLPlugInImageElement::pluginIsLoadable(const KURL& url, const String& mimeType) 299 { 300 Frame* frame = document()->frame(); 301 Settings* settings = frame->settings(); 302 if (!settings) 303 return false; 304 305 if (MIMETypeRegistry::isJavaAppletMIMEType(mimeType)) { 306 if (!settings->isJavaEnabled()) 307 return false; 308 } 309 310 if (document()->isSandboxed(SandboxPlugins)) 311 return false; 312 313 if (!document()->securityOrigin()->canDisplay(url)) { 314 FrameLoader::reportLocalLoadFailed(frame, url.string()); 315 return false; 316 } 317 318 String declaredMimeType = document()->isPluginDocument() && document()->ownerElement() ? 319 document()->ownerElement()->fastGetAttribute(HTMLNames::typeAttr) : 320 fastGetAttribute(HTMLNames::typeAttr); 321 if (!document()->contentSecurityPolicy()->allowObjectFromSource(url) 322 || !document()->contentSecurityPolicy()->allowPluginType(mimeType, declaredMimeType, url)) { 323 renderEmbeddedObject()->setPluginUnavailabilityReason(RenderEmbeddedObject::PluginBlockedByContentSecurityPolicy); 324 return false; 325 } 326 327 if (frame->loader() && !frame->loader()->mixedContentChecker()->canRunInsecureContent(document()->securityOrigin(), url)) 328 return false; 329 return true; 330 } 331 332 } // namespace WebCore 333