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 "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
56namespace WebCore {
57
58using namespace HTMLNames;
59
60SubframeLoader::SubframeLoader(Frame* frame)
61    : m_containsPlugins(false)
62    , m_frame(frame)
63{
64}
65
66void SubframeLoader::clear()
67{
68    m_containsPlugins = false;
69}
70
71bool 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
92bool 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
102bool 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
124bool 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)
150PassRefPtr<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
188PassRefPtr<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
226Frame* 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
236Frame* 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
291bool 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
300bool 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
323Document* SubframeLoader::document() const
324{
325    return m_frame->document();
326}
327
328bool 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
364KURL 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