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