Home | History | Annotate | Download | only in loader
      1 /*
      2  * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
      3  * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
      4  * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
      5  * Copyright (C) 2008 Alp Toker <alp (at) atoker.com>
      6  * Copyright (C) Research In Motion Limited 2009. All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  *
     12  * 1.  Redistributions of source code must retain the above copyright
     13  *     notice, this list of conditions and the following disclaimer.
     14  * 2.  Redistributions in binary form must reproduce the above copyright
     15  *     notice, this list of conditions and the following disclaimer in the
     16  *     documentation and/or other materials provided with the distribution.
     17  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
     18  *     its contributors may be used to endorse or promote products derived
     19  *     from this software without specific prior written permission.
     20  *
     21  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     22  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     23  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     24  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     25  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     26  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     27  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     28  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 #include "config.h"
     34 #include "SubframeLoader.h"
     35 
     36 #include "ContentSecurityPolicy.h"
     37 #include "Frame.h"
     38 #include "FrameLoaderClient.h"
     39 #include "HTMLAppletElement.h"
     40 #include "HTMLFrameElementBase.h"
     41 #include "HTMLNames.h"
     42 #include "HTMLPlugInImageElement.h"
     43 #include "MIMETypeRegistry.h"
     44 #include "Page.h"
     45 #include "PluginData.h"
     46 #include "PluginDocument.h"
     47 #include "RenderEmbeddedObject.h"
     48 #include "RenderView.h"
     49 #include "Settings.h"
     50 
     51 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
     52 #include "HTMLMediaElement.h"
     53 #include "RenderVideo.h"
     54 #endif
     55 
     56 namespace WebCore {
     57 
     58 using namespace HTMLNames;
     59 
     60 SubframeLoader::SubframeLoader(Frame* frame)
     61     : m_containsPlugins(false)
     62     , m_frame(frame)
     63 {
     64 }
     65 
     66 void SubframeLoader::clear()
     67 {
     68     m_containsPlugins = false;
     69 }
     70 
     71 bool SubframeLoader::requestFrame(HTMLFrameOwnerElement* ownerElement, const String& urlString, const AtomicString& frameName, bool lockHistory, bool lockBackForwardList)
     72 {
     73     // Support for <frame src="javascript:string">
     74     KURL scriptURL;
     75     KURL url;
     76     if (protocolIsJavaScript(urlString)) {
     77         scriptURL = completeURL(urlString); // completeURL() encodes the URL.
     78         url = blankURL();
     79     } else
     80         url = completeURL(urlString);
     81 
     82     Frame* frame = loadOrRedirectSubframe(ownerElement, url, frameName, lockHistory, lockBackForwardList);
     83     if (!frame)
     84         return false;
     85 
     86     if (!scriptURL.isEmpty())
     87         frame->script()->executeIfJavaScriptURL(scriptURL);
     88 
     89     return true;
     90 }
     91 
     92 bool SubframeLoader::resourceWillUsePlugin(const String& url, const String& mimeType, bool shouldPreferPlugInsForImages)
     93 {
     94     KURL completedURL;
     95     if (!url.isEmpty())
     96         completedURL = completeURL(url);
     97 
     98     bool useFallback;
     99     return shouldUsePlugin(completedURL, mimeType, shouldPreferPlugInsForImages, false, useFallback);
    100 }
    101 
    102 bool SubframeLoader::requestPlugin(HTMLPlugInImageElement* ownerElement, const KURL& url, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback)
    103 {
    104     Settings* settings = m_frame->settings();
    105     if ((!allowPlugins(AboutToInstantiatePlugin)
    106          // Application plug-ins are plug-ins implemented by the user agent, for example Qt plug-ins,
    107          // as opposed to third-party code such as Flash. The user agent decides whether or not they are
    108          // permitted, rather than WebKit.
    109          && !MIMETypeRegistry::isApplicationPluginMIMEType(mimeType))
    110         || (!settings->isJavaEnabled() && MIMETypeRegistry::isJavaAppletMIMEType(mimeType)))
    111         return false;
    112 
    113     if (m_frame->document()) {
    114         if (m_frame->document()->securityOrigin()->isSandboxed(SandboxPlugins))
    115             return false;
    116         if (!m_frame->document()->contentSecurityPolicy()->allowObjectFromSource(url))
    117             return false;
    118     }
    119 
    120     ASSERT(ownerElement->hasTagName(objectTag) || ownerElement->hasTagName(embedTag));
    121     return loadPlugin(ownerElement, url, mimeType, paramNames, paramValues, useFallback);
    122 }
    123 
    124 bool SubframeLoader::requestObject(HTMLPlugInImageElement* ownerElement, const String& url, const AtomicString& frameName, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues)
    125 {
    126     if (url.isEmpty() && mimeType.isEmpty())
    127         return false;
    128 
    129     // FIXME: None of this code should use renderers!
    130     RenderEmbeddedObject* renderer = ownerElement->renderEmbeddedObject();
    131     ASSERT(renderer);
    132     if (!renderer)
    133         return false;
    134 
    135     KURL completedURL;
    136     if (!url.isEmpty())
    137         completedURL = completeURL(url);
    138 
    139     bool useFallback;
    140     if (shouldUsePlugin(completedURL, mimeType, ownerElement->shouldPreferPlugInsForImages(), renderer->hasFallbackContent(), useFallback))
    141         return requestPlugin(ownerElement, completedURL, mimeType, paramNames, paramValues, useFallback);
    142 
    143     // If the plug-in element already contains a subframe, loadOrRedirectSubframe will re-use it. Otherwise,
    144     // it will create a new frame and set it as the RenderPart's widget, causing what was previously
    145     // in the widget to be torn down.
    146     return loadOrRedirectSubframe(ownerElement, completedURL, frameName, true, true);
    147 }
    148 
    149 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
    150 PassRefPtr<Widget> SubframeLoader::loadMediaPlayerProxyPlugin(Node* node, const KURL& url,
    151     const Vector<String>& paramNames, const Vector<String>& paramValues)
    152 {
    153     ASSERT(node->hasTagName(videoTag) || node->hasTagName(audioTag));
    154 
    155     KURL completedURL;
    156     if (!url.isEmpty())
    157         completedURL = completeURL(url);
    158 
    159     if (!m_frame->document()->securityOrigin()->canDisplay(completedURL)) {
    160         FrameLoader::reportLocalLoadFailed(m_frame, completedURL.string());
    161         return 0;
    162     }
    163 
    164     HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(node);
    165     RenderPart* renderer = toRenderPart(node->renderer());
    166     IntSize size;
    167 
    168     if (renderer)
    169         size = IntSize(renderer->contentWidth(), renderer->contentHeight());
    170     else if (mediaElement->isVideo())
    171         size = RenderVideo::defaultSize();
    172 
    173     m_frame->loader()->checkIfRunInsecureContent(m_frame->document()->securityOrigin(), completedURL);
    174 
    175     RefPtr<Widget> widget = m_frame->loader()->client()->createMediaPlayerProxyPlugin(size, mediaElement, completedURL,
    176                                          paramNames, paramValues, "application/x-media-element-proxy-plugin");
    177 
    178     if (widget && renderer) {
    179         renderer->setWidget(widget);
    180         renderer->node()->setNeedsStyleRecalc(SyntheticStyleChange);
    181     }
    182     m_containsPlugins = true;
    183 
    184     return widget ? widget.release() : 0;
    185 }
    186 #endif // ENABLE(PLUGIN_PROXY_FOR_VIDEO)
    187 
    188 PassRefPtr<Widget> SubframeLoader::createJavaAppletWidget(const IntSize& size, HTMLAppletElement* element, const HashMap<String, String>& args)
    189 {
    190     String baseURLString;
    191     String codeBaseURLString;
    192     Vector<String> paramNames;
    193     Vector<String> paramValues;
    194     HashMap<String, String>::const_iterator end = args.end();
    195     for (HashMap<String, String>::const_iterator it = args.begin(); it != end; ++it) {
    196         if (equalIgnoringCase(it->first, "baseurl"))
    197             baseURLString = it->second;
    198         else if (equalIgnoringCase(it->first, "codebase"))
    199             codeBaseURLString = it->second;
    200         paramNames.append(it->first);
    201         paramValues.append(it->second);
    202     }
    203 
    204     if (!codeBaseURLString.isEmpty()) {
    205         KURL codeBaseURL = completeURL(codeBaseURLString);
    206         if (!element->document()->securityOrigin()->canDisplay(codeBaseURL)) {
    207             FrameLoader::reportLocalLoadFailed(m_frame, codeBaseURL.string());
    208             return 0;
    209         }
    210     }
    211 
    212     if (baseURLString.isEmpty())
    213         baseURLString = m_frame->document()->baseURL().string();
    214     KURL baseURL = completeURL(baseURLString);
    215 
    216     RefPtr<Widget> widget;
    217     if (allowPlugins(AboutToInstantiatePlugin))
    218         widget = m_frame->loader()->client()->createJavaAppletWidget(size, element, baseURL, paramNames, paramValues);
    219     if (!widget)
    220         return 0;
    221 
    222     m_containsPlugins = true;
    223     return widget;
    224 }
    225 
    226 Frame* SubframeLoader::loadOrRedirectSubframe(HTMLFrameOwnerElement* ownerElement, const KURL& url, const AtomicString& frameName, bool lockHistory, bool lockBackForwardList)
    227 {
    228     Frame* frame = ownerElement->contentFrame();
    229     if (frame)
    230         frame->navigationScheduler()->scheduleLocationChange(m_frame->document()->securityOrigin(), url.string(), m_frame->loader()->outgoingReferrer(), lockHistory, lockBackForwardList);
    231     else
    232         frame = loadSubframe(ownerElement, url, frameName, m_frame->loader()->outgoingReferrer());
    233     return frame;
    234 }
    235 
    236 Frame* SubframeLoader::loadSubframe(HTMLFrameOwnerElement* ownerElement, const KURL& url, const String& name, const String& referrer)
    237 {
    238     bool allowsScrolling = true;
    239     int marginWidth = -1;
    240     int marginHeight = -1;
    241     if (ownerElement->hasTagName(frameTag) || ownerElement->hasTagName(iframeTag)) {
    242         HTMLFrameElementBase* o = static_cast<HTMLFrameElementBase*>(ownerElement);
    243         allowsScrolling = o->scrollingMode() != ScrollbarAlwaysOff;
    244         marginWidth = o->marginWidth();
    245         marginHeight = o->marginHeight();
    246     }
    247 
    248     if (!ownerElement->document()->securityOrigin()->canDisplay(url)) {
    249         FrameLoader::reportLocalLoadFailed(m_frame, url.string());
    250         return 0;
    251     }
    252 
    253     bool hideReferrer = SecurityOrigin::shouldHideReferrer(url, referrer);
    254     RefPtr<Frame> frame = m_frame->loader()->client()->createFrame(url, name, ownerElement, hideReferrer ? String() : referrer, allowsScrolling, marginWidth, marginHeight);
    255 
    256     if (!frame)  {
    257         m_frame->loader()->checkCallImplicitClose();
    258         return 0;
    259     }
    260 
    261     // All new frames will have m_isComplete set to true at this point due to synchronously loading
    262     // an empty document in FrameLoader::init(). But many frames will now be starting an
    263     // asynchronous load of url, so we set m_isComplete to false and then check if the load is
    264     // actually completed below. (Note that we set m_isComplete to false even for synchronous
    265     // loads, so that checkCompleted() below won't bail early.)
    266     // FIXME: Can we remove this entirely? m_isComplete normally gets set to false when a load is committed.
    267     frame->loader()->started();
    268 
    269     RenderObject* renderer = ownerElement->renderer();
    270     FrameView* view = frame->view();
    271     if (renderer && renderer->isWidget() && view)
    272         toRenderWidget(renderer)->setWidget(view);
    273 
    274     m_frame->loader()->checkCallImplicitClose();
    275 
    276     // Some loads are performed synchronously (e.g., about:blank and loads
    277     // cancelled by returning a null ResourceRequest from requestFromDelegate).
    278     // In these cases, the synchronous load would have finished
    279     // before we could connect the signals, so make sure to send the
    280     // completed() signal for the child by hand and mark the load as being
    281     // complete.
    282     // FIXME: In this case the Frame will have finished loading before
    283     // it's being added to the child list. It would be a good idea to
    284     // create the child first, then invoke the loader separately.
    285     if (frame->loader()->state() == FrameStateComplete && !frame->loader()->policyDocumentLoader())
    286         frame->loader()->checkCompleted();
    287 
    288     return frame.get();
    289 }
    290 
    291 bool SubframeLoader::allowPlugins(ReasonForCallingAllowPlugins reason)
    292 {
    293     Settings* settings = m_frame->settings();
    294     bool allowed = m_frame->loader()->client()->allowPlugins(settings && settings->arePluginsEnabled());
    295     if (!allowed && reason == AboutToInstantiatePlugin)
    296         m_frame->loader()->client()->didNotAllowPlugins();
    297     return allowed;
    298 }
    299 
    300 bool SubframeLoader::shouldUsePlugin(const KURL& url, const String& mimeType, bool shouldPreferPlugInsForImages, bool hasFallback, bool& useFallback)
    301 {
    302     if (m_frame->loader()->client()->shouldUsePluginDocument(mimeType)) {
    303         useFallback = false;
    304         return true;
    305     }
    306 
    307     // Allow other plug-ins to win over QuickTime because if the user has installed a plug-in that
    308     // can handle TIFF (which QuickTime can also handle) they probably intended to override QT.
    309     if (m_frame->page() && (mimeType == "image/tiff" || mimeType == "image/tif" || mimeType == "image/x-tiff")) {
    310         const PluginData* pluginData = m_frame->page()->pluginData();
    311         String pluginName = pluginData ? pluginData->pluginNameForMimeType(mimeType) : String();
    312         if (!pluginName.isEmpty() && !pluginName.contains("QuickTime", false))
    313             return true;
    314     }
    315 
    316     ObjectContentType objectType = m_frame->loader()->client()->objectContentType(url, mimeType, shouldPreferPlugInsForImages);
    317     // If an object's content can't be handled and it has no fallback, let
    318     // it be handled as a plugin to show the broken plugin icon.
    319     useFallback = objectType == ObjectContentNone && hasFallback;
    320     return objectType == ObjectContentNone || objectType == ObjectContentNetscapePlugin || objectType == ObjectContentOtherPlugin;
    321 }
    322 
    323 Document* SubframeLoader::document() const
    324 {
    325     return m_frame->document();
    326 }
    327 
    328 bool SubframeLoader::loadPlugin(HTMLPlugInImageElement* pluginElement, const KURL& url, const String& mimeType,
    329     const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback)
    330 {
    331     RenderEmbeddedObject* renderer = pluginElement->renderEmbeddedObject();
    332 
    333     // FIXME: This code should not depend on renderer!
    334     if (!renderer || useFallback)
    335         return false;
    336 
    337     if (!document()->securityOrigin()->canDisplay(url)) {
    338         FrameLoader::reportLocalLoadFailed(m_frame, url.string());
    339         return false;
    340     }
    341 
    342     FrameLoader* frameLoader = m_frame->loader();
    343     frameLoader->checkIfRunInsecureContent(document()->securityOrigin(), url);
    344 
    345     IntSize contentSize(renderer->contentWidth(), renderer->contentHeight());
    346     bool loadManually = document()->isPluginDocument() && !m_containsPlugins && toPluginDocument(document())->shouldLoadPluginManually();
    347     RefPtr<Widget> widget = frameLoader->client()->createPlugin(contentSize,
    348         pluginElement, url, paramNames, paramValues, mimeType, loadManually);
    349 
    350     if (!widget) {
    351         renderer->setShowsMissingPluginIndicator();
    352         return false;
    353     }
    354 
    355     renderer->setWidget(widget);
    356     m_containsPlugins = true;
    357 
    358 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) || ENABLE(3D_PLUGIN)
    359     pluginElement->setNeedsStyleRecalc(SyntheticStyleChange);
    360 #endif
    361     return true;
    362 }
    363 
    364 KURL SubframeLoader::completeURL(const String& url) const
    365 {
    366     ASSERT(m_frame->document());
    367     return m_frame->document()->completeURL(url);
    368 }
    369 
    370 } // namespace WebCore
    371