2010-09-10 Dirk Pranke <dpranke@chromium.org>
[WebKit.git] / WebCore / loader / SubframeLoader.cpp
1 /*
2  * Copyright (C) 2006, 2007, 2008, 2009, 2010 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 "Frame.h"
37 #include "FrameLoaderClient.h"
38 #include "HTMLAppletElement.h"
39 #include "HTMLFrameElementBase.h"
40 #include "HTMLNames.h"
41 #include "HTMLPlugInImageElement.h"
42 #include "MIMETypeRegistry.h"
43 #include "Page.h"
44 #include "PluginData.h"
45 #include "RenderEmbeddedObject.h"
46 #include "RenderView.h"
47 #include "Settings.h"
48 #include "XSSAuditor.h"
49
50 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
51 #include "HTMLMediaElement.h"
52 #include "RenderVideo.h"
53 #endif
54
55 namespace WebCore {
56     
57 using namespace HTMLNames;
58
59 SubframeLoader::SubframeLoader(Frame* frame)
60     : m_containsPlugins(false)
61     , m_frame(frame)
62 {
63 }
64
65 void SubframeLoader::clear()
66 {
67     m_containsPlugins = false;
68 }
69
70 bool SubframeLoader::requestFrame(HTMLFrameOwnerElement* ownerElement, const String& urlString, const AtomicString& frameName, bool lockHistory, bool lockBackForwardList)
71 {
72     // Support for <frame src="javascript:string">
73     KURL scriptURL;
74     KURL url;
75     if (protocolIsJavaScript(urlString)) {
76         scriptURL = completeURL(urlString); // completeURL() encodes the URL.
77         url = blankURL();
78     } else
79         url = completeURL(urlString);
80
81     Frame* frame = loadOrRedirectSubframe(ownerElement, url, frameName, lockHistory, lockBackForwardList);
82     if (!frame)
83         return false;
84
85     if (!scriptURL.isEmpty())
86         frame->script()->executeIfJavaScriptURL(scriptURL);
87
88     return true;
89 }
90     
91 bool SubframeLoader::resourceWillUsePlugin(const String& url, const String& mimeType)
92 {
93     KURL completedURL;
94     if (!url.isEmpty())
95         completedURL = completeURL(url);
96     bool useFallback;
97     return shouldUsePlugin(completedURL, mimeType, false, useFallback);
98 }
99
100 bool SubframeLoader::requestObject(HTMLPlugInImageElement* ownerElement, const String& url, const AtomicString& frameName,
101     const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues)
102 {
103     if (url.isEmpty() && mimeType.isEmpty())
104         return false;
105     
106     if (!m_frame->script()->xssAuditor()->canLoadObject(url)) {
107         // It is unsafe to honor the request for this object.
108         return false;
109     }
110
111     // FIXME: None of this code should use renderers!
112     RenderEmbeddedObject* renderer = ownerElement->renderEmbeddedObject();
113     ASSERT(renderer);
114     if (!renderer)
115         return false;
116
117     KURL completedURL;
118     if (!url.isEmpty())
119         completedURL = completeURL(url);
120
121     bool useFallback;
122     if (shouldUsePlugin(completedURL, mimeType, renderer->hasFallbackContent(), useFallback)) {
123         Settings* settings = m_frame->settings();
124         if ((!allowPlugins(AboutToInstantiatePlugin)
125              // Application plugins are plugins implemented by the user agent, for example Qt plugins,
126              // as opposed to third-party code such as flash. The user agent decides whether or not they are
127              // permitted, rather than WebKit.
128              && !MIMETypeRegistry::isApplicationPluginMIMEType(mimeType))
129             || (!settings->isJavaEnabled() && MIMETypeRegistry::isJavaAppletMIMEType(mimeType)))
130             return false;
131         if (m_frame->document() && m_frame->document()->securityOrigin()->isSandboxed(SandboxPlugins))
132             return false;
133
134         ASSERT(ownerElement->hasTagName(objectTag) || ownerElement->hasTagName(embedTag));
135         HTMLPlugInImageElement* pluginElement = static_cast<HTMLPlugInImageElement*>(ownerElement);
136
137         return loadPlugin(pluginElement, completedURL, mimeType, paramNames, paramValues, useFallback);
138     }
139
140     // If the plug-in element already contains a subframe, loadOrRedirectSubframe will re-use it. Otherwise,
141     // it will create a new frame and set it as the RenderPart's widget, causing what was previously 
142     // in the widget to be torn down.
143     return loadOrRedirectSubframe(ownerElement, completedURL, frameName, true, true);
144 }
145
146 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
147 PassRefPtr<Widget> SubframeLoader::loadMediaPlayerProxyPlugin(Node* node, const KURL& url,
148     const Vector<String>& paramNames, const Vector<String>& paramValues)
149 {
150     ASSERT(node->hasTagName(videoTag) || node->hasTagName(audioTag));
151
152     if (!m_frame->script()->xssAuditor()->canLoadObject(url.string()))
153         return 0;
154
155     KURL completedURL;
156     if (!url.isEmpty())
157         completedURL = completeURL(url);
158
159     if (!SecurityOrigin::canDisplay(completedURL, String(), m_frame->document())) {
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
187 void FrameLoader::hideMediaPlayerProxyPlugin(Widget* widget)
188 {
189     m_client->hideMediaPlayerProxyPlugin(widget);
190 }
191
192 void FrameLoader::showMediaPlayerProxyPlugin(Widget* widget)
193 {
194     m_client->showMediaPlayerProxyPlugin(widget);
195 }
196
197 #endif // ENABLE(PLUGIN_PROXY_FOR_VIDEO)
198
199 PassRefPtr<Widget> SubframeLoader::createJavaAppletWidget(const IntSize& size, HTMLAppletElement* element, const HashMap<String, String>& args)
200 {
201     String baseURLString;
202     String codeBaseURLString;
203     Vector<String> paramNames;
204     Vector<String> paramValues;
205     HashMap<String, String>::const_iterator end = args.end();
206     for (HashMap<String, String>::const_iterator it = args.begin(); it != end; ++it) {
207         if (equalIgnoringCase(it->first, "baseurl"))
208             baseURLString = it->second;
209         else if (equalIgnoringCase(it->first, "codebase"))
210             codeBaseURLString = it->second;
211         paramNames.append(it->first);
212         paramValues.append(it->second);
213     }
214
215     if (!codeBaseURLString.isEmpty()) {
216         KURL codeBaseURL = completeURL(codeBaseURLString);
217         if (!SecurityOrigin::canDisplay(codeBaseURL, String(), element->document())) {
218             FrameLoader::reportLocalLoadFailed(m_frame, codeBaseURL.string());
219             return 0;
220         }
221     }
222
223     if (baseURLString.isEmpty())
224         baseURLString = m_frame->document()->baseURL().string();
225     KURL baseURL = completeURL(baseURLString);
226
227     RefPtr<Widget> widget;
228     if (allowPlugins(AboutToInstantiatePlugin))
229         widget = m_frame->loader()->client()->createJavaAppletWidget(size, element, baseURL, paramNames, paramValues);
230     if (!widget)
231         return 0;
232
233     m_containsPlugins = true;
234     return widget;
235 }
236
237 Frame* SubframeLoader::loadOrRedirectSubframe(HTMLFrameOwnerElement* ownerElement, const KURL& url, const AtomicString& frameName, bool lockHistory, bool lockBackForwardList)
238 {
239     Frame* frame = ownerElement->contentFrame();
240     if (frame)
241         frame->redirectScheduler()->scheduleLocationChange(url.string(), m_frame->loader()->outgoingReferrer(), lockHistory, lockBackForwardList, m_frame->loader()->isProcessingUserGesture());
242     else
243         frame = loadSubframe(ownerElement, url, frameName, m_frame->loader()->outgoingReferrer());
244     return frame;
245 }
246
247 Frame* SubframeLoader::loadSubframe(HTMLFrameOwnerElement* ownerElement, const KURL& url, const String& name, const String& referrer)
248 {
249     bool allowsScrolling = true;
250     int marginWidth = -1;
251     int marginHeight = -1;
252     if (ownerElement->hasTagName(frameTag) || ownerElement->hasTagName(iframeTag)) {
253         HTMLFrameElementBase* o = static_cast<HTMLFrameElementBase*>(ownerElement);
254         allowsScrolling = o->scrollingMode() != ScrollbarAlwaysOff;
255         marginWidth = o->getMarginWidth();
256         marginHeight = o->getMarginHeight();
257     }
258
259     if (!SecurityOrigin::canDisplay(url, String(), ownerElement->document())) {
260         FrameLoader::reportLocalLoadFailed(m_frame, url.string());
261         return 0;
262     }
263
264     bool hideReferrer = SecurityOrigin::shouldHideReferrer(url, referrer);
265     RefPtr<Frame> frame = m_frame->loader()->client()->createFrame(url, name, ownerElement, hideReferrer ? String() : referrer, allowsScrolling, marginWidth, marginHeight);
266
267     if (!frame)  {
268         m_frame->loader()->checkCallImplicitClose();
269         return 0;
270     }
271     
272     // All new frames will have m_isComplete set to true at this point due to synchronously loading
273     // an empty document in FrameLoader::init(). But many frames will now be starting an
274     // asynchronous load of url, so we set m_isComplete to false and then check if the load is
275     // actually completed below. (Note that we set m_isComplete to false even for synchronous
276     // loads, so that checkCompleted() below won't bail early.)
277     // FIXME: Can we remove this entirely? m_isComplete normally gets set to false when a load is committed.
278     frame->loader()->started();
279    
280     RenderObject* renderer = ownerElement->renderer();
281     FrameView* view = frame->view();
282     if (renderer && renderer->isWidget() && view)
283         toRenderWidget(renderer)->setWidget(view);
284     
285     m_frame->loader()->checkCallImplicitClose();
286     
287     // Some loads are performed synchronously (e.g., about:blank and loads
288     // cancelled by returning a null ResourceRequest from requestFromDelegate).
289     // In these cases, the synchronous load would have finished
290     // before we could connect the signals, so make sure to send the 
291     // completed() signal for the child by hand and mark the load as being
292     // complete.
293     // FIXME: In this case the Frame will have finished loading before 
294     // it's being added to the child list. It would be a good idea to
295     // create the child first, then invoke the loader separately.
296     if (frame->loader()->state() == FrameStateComplete && !frame->loader()->policyDocumentLoader())
297         frame->loader()->checkCompleted();
298
299     return frame.get();
300 }
301
302 bool SubframeLoader::allowPlugins(ReasonForCallingAllowPlugins reason)
303 {
304     Settings* settings = m_frame->settings();
305     bool allowed = m_frame->loader()->client()->allowPlugins(settings && settings->arePluginsEnabled());
306     if (!allowed && reason == AboutToInstantiatePlugin)
307         m_frame->loader()->client()->didNotAllowPlugins();
308     return allowed;
309 }
310
311 bool SubframeLoader::shouldUsePlugin(const KURL& url, const String& mimeType, bool hasFallback, bool& useFallback)
312 {
313     if (m_frame->loader()->client()->shouldUsePluginDocument(mimeType)) {
314         useFallback = false;
315         return true;
316     }
317
318     // Allow other plug-ins to win over QuickTime because if the user has installed a plug-in that
319     // can handle TIFF (which QuickTime can also handle) they probably intended to override QT.
320     if (m_frame->page() && (mimeType == "image/tiff" || mimeType == "image/tif" || mimeType == "image/x-tiff")) {
321         const PluginData* pluginData = m_frame->page()->pluginData();
322         String pluginName = pluginData ? pluginData->pluginNameForMimeType(mimeType) : String();
323         if (!pluginName.isEmpty() && !pluginName.contains("QuickTime", false)) 
324             return true;
325     }
326         
327     ObjectContentType objectType = m_frame->loader()->client()->objectContentType(url, mimeType);
328     // If an object's content can't be handled and it has no fallback, let
329     // it be handled as a plugin to show the broken plugin icon.
330     useFallback = objectType == ObjectContentNone && hasFallback;
331     return objectType == ObjectContentNone || objectType == ObjectContentNetscapePlugin || objectType == ObjectContentOtherPlugin;
332 }
333
334 Document* SubframeLoader::document() const
335 {
336     return m_frame->document();
337 }
338
339 bool SubframeLoader::loadPlugin(HTMLPlugInImageElement* pluginElement, const KURL& url, const String& mimeType,
340     const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback)
341 {
342     RenderEmbeddedObject* renderer = pluginElement->renderEmbeddedObject();
343
344     // FIXME: This code should not depend on renderer!
345     if (!renderer || useFallback)
346         return false;
347
348     if (!SecurityOrigin::canDisplay(url, String(), document())) {
349         FrameLoader::reportLocalLoadFailed(m_frame, url.string());
350         return false;
351     }
352
353     FrameLoader* frameLoader = m_frame->loader();
354     frameLoader->checkIfRunInsecureContent(document()->securityOrigin(), url);
355
356     IntSize contentSize(renderer->contentWidth(), renderer->contentHeight());
357     bool loadManually = document()->isPluginDocument() && !m_containsPlugins;
358     RefPtr<Widget> widget = frameLoader->client()->createPlugin(contentSize,
359         pluginElement, url, paramNames, paramValues, mimeType, loadManually);
360
361     if (!widget) {
362         renderer->setShowsMissingPluginIndicator();
363         return false;
364     }
365
366     renderer->setWidget(widget);
367     m_containsPlugins = true;
368
369 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
370     pluginElement->setNeedsStyleRecalc(SyntheticStyleChange);
371 #endif
372     return true;
373 }
374
375 KURL SubframeLoader::completeURL(const String& url) const
376 {
377     ASSERT(m_frame->document());
378     return m_frame->document()->completeURL(url);
379 }
380
381 } // namespace WebCore