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@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 "Chrome.h"
37#include "ChromeClient.h"
38#include "ContentSecurityPolicy.h"
39#include "DiagnosticLoggingKeys.h"
40#include "Frame.h"
41#include "FrameLoader.h"
42#include "FrameLoaderClient.h"
43#include "HTMLAppletElement.h"
44#include "HTMLFrameElementBase.h"
45#include "HTMLNames.h"
46#include "HTMLPlugInImageElement.h"
47#include "MIMETypeRegistry.h"
48#include "Page.h"
49#include "PluginData.h"
50#include "PluginDocument.h"
51#include "RenderEmbeddedObject.h"
52#include "RenderView.h"
53#include "ScriptController.h"
54#include "SecurityOrigin.h"
55#include "SecurityPolicy.h"
56#include "Settings.h"
57
58#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
59#include "HTMLMediaElement.h"
60#include "RenderVideo.h"
61#endif
62
63namespace WebCore {
64
65using namespace HTMLNames;
66
67SubframeLoader::SubframeLoader(Frame* frame)
68    : m_containsPlugins(false)
69    , m_frame(frame)
70{
71}
72
73void SubframeLoader::clear()
74{
75    m_containsPlugins = false;
76}
77
78bool SubframeLoader::requestFrame(HTMLFrameOwnerElement* ownerElement, const String& urlString, const AtomicString& frameName, bool lockHistory, bool lockBackForwardList)
79{
80    // Support for <frame src="javascript:string">
81    KURL scriptURL;
82    KURL url;
83    if (protocolIsJavaScript(urlString)) {
84        scriptURL = completeURL(urlString); // completeURL() encodes the URL.
85        url = blankURL();
86    } else
87        url = completeURL(urlString);
88
89    Frame* frame = loadOrRedirectSubframe(ownerElement, url, frameName, lockHistory, lockBackForwardList);
90    if (!frame)
91        return false;
92
93    if (!scriptURL.isEmpty())
94        frame->script()->executeIfJavaScriptURL(scriptURL);
95
96    return true;
97}
98
99bool SubframeLoader::resourceWillUsePlugin(const String& url, const String& mimeType, bool shouldPreferPlugInsForImages)
100{
101    KURL completedURL;
102    if (!url.isEmpty())
103        completedURL = completeURL(url);
104
105    bool useFallback;
106    return shouldUsePlugin(completedURL, mimeType, shouldPreferPlugInsForImages, false, useFallback);
107}
108
109bool SubframeLoader::pluginIsLoadable(HTMLPlugInImageElement* pluginElement, const KURL& url, const String& mimeType)
110{
111    Settings* settings = m_frame->settings();
112    if (!settings)
113        return false;
114
115    if (MIMETypeRegistry::isJavaAppletMIMEType(mimeType)) {
116        if (!settings->isJavaEnabled())
117            return false;
118        if (document() && document()->securityOrigin()->isLocal() && !settings->isJavaEnabledForLocalFiles())
119            return false;
120    }
121
122    if (document()) {
123        if (document()->isSandboxed(SandboxPlugins))
124            return false;
125
126        if (!document()->securityOrigin()->canDisplay(url)) {
127            FrameLoader::reportLocalLoadFailed(m_frame, url.string());
128            return false;
129        }
130
131        String declaredMimeType = document()->isPluginDocument() && document()->ownerElement() ?
132            document()->ownerElement()->fastGetAttribute(HTMLNames::typeAttr) :
133            pluginElement->fastGetAttribute(HTMLNames::typeAttr);
134        if (!document()->contentSecurityPolicy()->allowObjectFromSource(url)
135            || !document()->contentSecurityPolicy()->allowPluginType(mimeType, declaredMimeType, url)) {
136            RenderEmbeddedObject* renderer = pluginElement->renderEmbeddedObject();
137            renderer->setPluginUnavailabilityReason(RenderEmbeddedObject::PluginBlockedByContentSecurityPolicy);
138            return false;
139        }
140
141        if (m_frame->loader() && !m_frame->loader()->mixedContentChecker()->canRunInsecureContent(document()->securityOrigin(), url))
142            return false;
143    }
144
145    return true;
146}
147
148bool SubframeLoader::requestPlugin(HTMLPlugInImageElement* ownerElement, const KURL& url, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback)
149{
150    // Application plug-ins are plug-ins implemented by the user agent, for example Qt plug-ins,
151    // as opposed to third-party code such as Flash. The user agent decides whether or not they are
152    // permitted, rather than WebKit.
153    if ((!allowPlugins(AboutToInstantiatePlugin) && !MIMETypeRegistry::isApplicationPluginMIMEType(mimeType)))
154        return false;
155
156    if (!pluginIsLoadable(ownerElement, url, mimeType))
157        return false;
158
159    ASSERT(ownerElement->hasTagName(objectTag) || ownerElement->hasTagName(embedTag));
160    return loadPlugin(ownerElement, url, mimeType, paramNames, paramValues, useFallback);
161}
162
163static String findPluginMIMETypeFromURL(Page* page, const String& url)
164{
165    if (!url)
166        return String();
167
168    size_t dotIndex = url.reverseFind('.');
169    if (dotIndex == notFound)
170        return String();
171
172    String extension = url.substring(dotIndex + 1);
173
174    PluginData* pluginData = page->pluginData();
175    if (!pluginData)
176        return String();
177
178    for (size_t i = 0; i < pluginData->mimes().size(); ++i) {
179        const MimeClassInfo& mimeClassInfo = pluginData->mimes()[i];
180        for (size_t j = 0; j < mimeClassInfo.extensions.size(); ++j) {
181            if (equalIgnoringCase(extension, mimeClassInfo.extensions[j]))
182                return mimeClassInfo.type;
183        }
184    }
185
186    return String();
187}
188
189static void logPluginRequest(Page* page, const String& mimeType, const String& url, bool success)
190{
191    if (!page || !page->settings()->diagnosticLoggingEnabled())
192        return;
193
194    String newMIMEType = mimeType;
195    if (!newMIMEType) {
196        // Try to figure out the MIME type from the URL extension.
197        newMIMEType = findPluginMIMETypeFromURL(page, url);
198        if (!newMIMEType)
199            return;
200    }
201
202    PluginData* pluginData = page->pluginData();
203    String pluginFile = pluginData ? pluginData->pluginFileForMimeType(newMIMEType) : String();
204    String description = !pluginFile ? newMIMEType : pluginFile;
205
206    ChromeClient* client = page->chrome().client();
207    client->logDiagnosticMessage(success ? DiagnosticLoggingKeys::pluginLoadedKey() : DiagnosticLoggingKeys::pluginLoadingFailedKey(), description, DiagnosticLoggingKeys::noopKey());
208
209    if (!page->hasSeenAnyPlugin())
210        client->logDiagnosticMessage(DiagnosticLoggingKeys::pageContainsAtLeastOnePluginKey(), emptyString(), DiagnosticLoggingKeys::noopKey());
211
212    if (!page->hasSeenPlugin(description))
213        client->logDiagnosticMessage(DiagnosticLoggingKeys::pageContainsPluginKey(), description, DiagnosticLoggingKeys::noopKey());
214
215    page->sawPlugin(description);
216}
217
218bool SubframeLoader::requestObject(HTMLPlugInImageElement* ownerElement, const String& url, const AtomicString& frameName, 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 = ownerElement->renderEmbeddedObject();
225    ASSERT(renderer);
226    if (!renderer)
227        return false;
228
229    KURL completedURL;
230    if (!url.isEmpty())
231        completedURL = completeURL(url);
232
233    bool useFallback;
234    if (shouldUsePlugin(completedURL, mimeType, ownerElement->shouldPreferPlugInsForImages(), renderer->hasFallbackContent(), useFallback)) {
235        bool success = requestPlugin(ownerElement, completedURL, mimeType, paramNames, paramValues, useFallback);
236        logPluginRequest(document()->page(), mimeType, completedURL, success);
237        return success;
238    }
239
240    // If the plug-in element already contains a subframe, loadOrRedirectSubframe will re-use it. Otherwise,
241    // it will create a new frame and set it as the RenderPart's widget, causing what was previously
242    // in the widget to be torn down.
243    return loadOrRedirectSubframe(ownerElement, completedURL, frameName, true, true);
244}
245
246#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
247PassRefPtr<Widget> SubframeLoader::loadMediaPlayerProxyPlugin(Node* node, const KURL& url,
248    const Vector<String>& paramNames, const Vector<String>& paramValues)
249{
250    ASSERT(node->hasTagName(videoTag) || node->hasTagName(audioTag));
251
252    KURL completedURL;
253    if (!url.isEmpty())
254        completedURL = completeURL(url);
255
256    if (!m_frame->document()->securityOrigin()->canDisplay(completedURL)) {
257        FrameLoader::reportLocalLoadFailed(m_frame, completedURL.string());
258        return 0;
259    }
260
261    if (!m_frame->document()->contentSecurityPolicy()->allowMediaFromSource(completedURL))
262        return 0;
263
264    HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(node);
265    RenderPart* renderer = toRenderPart(node->renderer());
266    IntSize size;
267
268    if (renderer)
269        size = roundedIntSize(LayoutSize(renderer->contentWidth(), renderer->contentHeight()));
270    else if (mediaElement->isVideo())
271        size = RenderVideo::defaultSize();
272
273    if (!m_frame->loader()->mixedContentChecker()->canRunInsecureContent(m_frame->document()->securityOrigin(), completedURL))
274        return 0;
275
276    RefPtr<Widget> widget = m_frame->loader()->client()->createMediaPlayerProxyPlugin(size, mediaElement, completedURL,
277                                         paramNames, paramValues, "application/x-media-element-proxy-plugin");
278
279    if (widget && renderer) {
280        renderer->setWidget(widget);
281        renderer->node()->setNeedsStyleRecalc(SyntheticStyleChange);
282    }
283    m_containsPlugins = true;
284
285    return widget ? widget.release() : 0;
286}
287#endif // ENABLE(PLUGIN_PROXY_FOR_VIDEO)
288
289PassRefPtr<Widget> SubframeLoader::createJavaAppletWidget(const IntSize& size, HTMLAppletElement* element, const Vector<String>& paramNames, const Vector<String>& paramValues)
290{
291    String baseURLString;
292    String codeBaseURLString;
293
294    for (size_t i = 0; i < paramNames.size(); ++i) {
295        if (equalIgnoringCase(paramNames[i], "baseurl"))
296            baseURLString = paramValues[i];
297        else if (equalIgnoringCase(paramNames[i], "codebase"))
298            codeBaseURLString = paramValues[i];
299    }
300
301    if (!codeBaseURLString.isEmpty()) {
302        KURL codeBaseURL = completeURL(codeBaseURLString);
303        if (!element->document()->securityOrigin()->canDisplay(codeBaseURL)) {
304            FrameLoader::reportLocalLoadFailed(m_frame, codeBaseURL.string());
305            return 0;
306        }
307
308        const char javaAppletMimeType[] = "application/x-java-applet";
309        if (!element->document()->contentSecurityPolicy()->allowObjectFromSource(codeBaseURL)
310            || !element->document()->contentSecurityPolicy()->allowPluginType(javaAppletMimeType, javaAppletMimeType, codeBaseURL))
311            return 0;
312    }
313
314    if (baseURLString.isEmpty())
315        baseURLString = m_frame->document()->baseURL().string();
316    KURL baseURL = completeURL(baseURLString);
317
318    RefPtr<Widget> widget;
319    if (allowPlugins(AboutToInstantiatePlugin))
320        widget = m_frame->loader()->client()->createJavaAppletWidget(size, element, baseURL, paramNames, paramValues);
321
322    logPluginRequest(document()->page(), element->serviceType(), String(), widget);
323
324    if (!widget) {
325        RenderEmbeddedObject* renderer = element->renderEmbeddedObject();
326
327        if (!renderer->isPluginUnavailable())
328            renderer->setPluginUnavailabilityReason(RenderEmbeddedObject::PluginMissing);
329        return 0;
330    }
331
332    m_containsPlugins = true;
333    return widget;
334}
335
336Frame* SubframeLoader::loadOrRedirectSubframe(HTMLFrameOwnerElement* ownerElement, const KURL& url, const AtomicString& frameName, bool lockHistory, bool lockBackForwardList)
337{
338    Frame* frame = ownerElement->contentFrame();
339    if (frame)
340        frame->navigationScheduler()->scheduleLocationChange(m_frame->document()->securityOrigin(), url.string(), m_frame->loader()->outgoingReferrer(), lockHistory, lockBackForwardList);
341    else
342        frame = loadSubframe(ownerElement, url, frameName, m_frame->loader()->outgoingReferrer());
343
344    if (!frame)
345        return nullptr;
346
347    ASSERT(ownerElement->contentFrame() == frame || !ownerElement->contentFrame());
348    return ownerElement->contentFrame();
349}
350
351Frame* SubframeLoader::loadSubframe(HTMLFrameOwnerElement* ownerElement, const KURL& url, const String& name, const String& referrer)
352{
353    RefPtr<Frame> protect(m_frame);
354
355    bool allowsScrolling = true;
356    int marginWidth = -1;
357    int marginHeight = -1;
358    if (ownerElement->hasTagName(frameTag) || ownerElement->hasTagName(iframeTag)) {
359        HTMLFrameElementBase* o = static_cast<HTMLFrameElementBase*>(ownerElement);
360        allowsScrolling = o->scrollingMode() != ScrollbarAlwaysOff;
361        marginWidth = o->marginWidth();
362        marginHeight = o->marginHeight();
363    }
364
365    if (!ownerElement->document()->securityOrigin()->canDisplay(url)) {
366        FrameLoader::reportLocalLoadFailed(m_frame, url.string());
367        return 0;
368    }
369
370    if (!SubframeLoadingDisabler::canLoadFrame(ownerElement))
371        return nullptr;
372
373    String referrerToUse = SecurityPolicy::generateReferrerHeader(ownerElement->document()->referrerPolicy(), url, referrer);
374    RefPtr<Frame> frame = m_frame->loader()->client()->createFrame(url, name, ownerElement, referrerToUse, allowsScrolling, marginWidth, marginHeight);
375
376    if (!frame)  {
377        m_frame->loader()->checkCallImplicitClose();
378        return 0;
379    }
380
381    // All new frames will have m_isComplete set to true at this point due to synchronously loading
382    // an empty document in FrameLoader::init(). But many frames will now be starting an
383    // asynchronous load of url, so we set m_isComplete to false and then check if the load is
384    // actually completed below. (Note that we set m_isComplete to false even for synchronous
385    // loads, so that checkCompleted() below won't bail early.)
386    // FIXME: Can we remove this entirely? m_isComplete normally gets set to false when a load is committed.
387    frame->loader()->started();
388
389    RenderObject* renderer = ownerElement->renderer();
390    FrameView* view = frame->view();
391    if (renderer && renderer->isWidget() && view)
392        toRenderWidget(renderer)->setWidget(view);
393
394    m_frame->loader()->checkCallImplicitClose();
395
396    // Some loads are performed synchronously (e.g., about:blank and loads
397    // cancelled by returning a null ResourceRequest from requestFromDelegate).
398    // In these cases, the synchronous load would have finished
399    // before we could connect the signals, so make sure to send the
400    // completed() signal for the child by hand and mark the load as being
401    // complete.
402    // FIXME: In this case the Frame will have finished loading before
403    // it's being added to the child list. It would be a good idea to
404    // create the child first, then invoke the loader separately.
405    if (frame->loader()->state() == FrameStateComplete && !frame->loader()->policyDocumentLoader())
406        frame->loader()->checkCompleted();
407
408    return frame.get();
409}
410
411bool SubframeLoader::allowPlugins(ReasonForCallingAllowPlugins reason)
412{
413    Settings* settings = m_frame->settings();
414    bool allowed = m_frame->loader()->client()->allowPlugins(settings && settings->arePluginsEnabled());
415    if (!allowed && reason == AboutToInstantiatePlugin)
416        m_frame->loader()->client()->didNotAllowPlugins();
417    return allowed;
418}
419
420bool SubframeLoader::shouldUsePlugin(const KURL& url, const String& mimeType, bool shouldPreferPlugInsForImages, bool hasFallback, bool& useFallback)
421{
422    if (m_frame->loader()->client()->shouldAlwaysUsePluginDocument(mimeType)) {
423        useFallback = false;
424        return true;
425    }
426
427    // Allow other plug-ins to win over QuickTime because if the user has installed a plug-in that
428    // can handle TIFF (which QuickTime can also handle) they probably intended to override QT.
429    if (m_frame->page() && (mimeType == "image/tiff" || mimeType == "image/tif" || mimeType == "image/x-tiff")) {
430        const PluginData* pluginData = m_frame->page()->pluginData();
431        String pluginName = pluginData ? pluginData->pluginNameForMimeType(mimeType) : String();
432        if (!pluginName.isEmpty() && !pluginName.contains("QuickTime", false))
433            return true;
434    }
435
436    ObjectContentType objectType = m_frame->loader()->client()->objectContentType(url, mimeType, shouldPreferPlugInsForImages);
437    // If an object's content can't be handled and it has no fallback, let
438    // it be handled as a plugin to show the broken plugin icon.
439    useFallback = objectType == ObjectContentNone && hasFallback;
440    return objectType == ObjectContentNone || objectType == ObjectContentNetscapePlugin || objectType == ObjectContentOtherPlugin;
441}
442
443Document* SubframeLoader::document() const
444{
445    return m_frame->document();
446}
447
448bool SubframeLoader::loadPlugin(HTMLPlugInImageElement* pluginElement, const KURL& url, const String& mimeType,
449    const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback)
450{
451    RenderEmbeddedObject* renderer = pluginElement->renderEmbeddedObject();
452
453    // FIXME: This code should not depend on renderer!
454    if (!renderer || useFallback)
455        return false;
456
457    pluginElement->subframeLoaderWillCreatePlugIn(url);
458
459    IntSize contentSize = roundedIntSize(LayoutSize(renderer->contentWidth(), renderer->contentHeight()));
460    bool loadManually = document()->isPluginDocument() && !m_containsPlugins && toPluginDocument(document())->shouldLoadPluginManually();
461    RefPtr<Widget> widget = m_frame->loader()->client()->createPlugin(contentSize,
462        pluginElement, url, paramNames, paramValues, mimeType, loadManually);
463
464    if (!widget) {
465        if (!renderer->isPluginUnavailable())
466            renderer->setPluginUnavailabilityReason(RenderEmbeddedObject::PluginMissing);
467        return false;
468    }
469
470    pluginElement->subframeLoaderDidCreatePlugIn(widget.get());
471    renderer->setWidget(widget);
472    m_containsPlugins = true;
473
474#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
475    pluginElement->setNeedsStyleRecalc(SyntheticStyleChange);
476#endif
477    return true;
478}
479
480KURL SubframeLoader::completeURL(const String& url) const
481{
482    ASSERT(m_frame->document());
483    return m_frame->document()->completeURL(url);
484}
485
486} // namespace WebCore
487