Home | History | Annotate | Download | only in html
      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