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 "SecurityOrigin.h"
50 #include "Settings.h"
51
52 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
53 #include "HTMLMediaElement.h"
54 #include "RenderVideo.h"
55 #endif
56
57 namespace WebCore {
58
59 using namespace HTMLNames;
60
SubframeLoader(Frame * frame)61 SubframeLoader::SubframeLoader(Frame* frame)
62 : m_containsPlugins(false)
63 , m_frame(frame)
64 {
65 }
66
clear()67 void SubframeLoader::clear()
68 {
69 m_containsPlugins = false;
70 }
71
requestFrame(HTMLFrameOwnerElement * ownerElement,const String & urlString,const AtomicString & frameName,bool lockHistory,bool lockBackForwardList)72 bool SubframeLoader::requestFrame(HTMLFrameOwnerElement* ownerElement, const String& urlString, const AtomicString& frameName, bool lockHistory, bool lockBackForwardList)
73 {
74 // Support for <frame src="javascript:string">
75 KURL scriptURL;
76 KURL url;
77 if (protocolIsJavaScript(urlString)) {
78 scriptURL = completeURL(urlString); // completeURL() encodes the URL.
79 url = blankURL();
80 } else
81 url = completeURL(urlString);
82
83 Frame* frame = loadOrRedirectSubframe(ownerElement, url, frameName, lockHistory, lockBackForwardList);
84 if (!frame)
85 return false;
86
87 if (!scriptURL.isEmpty())
88 frame->script()->executeIfJavaScriptURL(scriptURL);
89
90 return true;
91 }
92
resourceWillUsePlugin(const String & url,const String & mimeType,bool shouldPreferPlugInsForImages)93 bool SubframeLoader::resourceWillUsePlugin(const String& url, const String& mimeType, bool shouldPreferPlugInsForImages)
94 {
95 KURL completedURL;
96 if (!url.isEmpty())
97 completedURL = completeURL(url);
98
99 bool useFallback;
100 return shouldUsePlugin(completedURL, mimeType, shouldPreferPlugInsForImages, false, useFallback);
101 }
102
requestPlugin(HTMLPlugInImageElement * ownerElement,const KURL & url,const String & mimeType,const Vector<String> & paramNames,const Vector<String> & paramValues,bool useFallback)103 bool SubframeLoader::requestPlugin(HTMLPlugInImageElement* ownerElement, const KURL& url, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback)
104 {
105 Settings* settings = m_frame->settings();
106 if ((!allowPlugins(AboutToInstantiatePlugin)
107 // Application plug-ins are plug-ins implemented by the user agent, for example Qt plug-ins,
108 // as opposed to third-party code such as Flash. The user agent decides whether or not they are
109 // permitted, rather than WebKit.
110 && !MIMETypeRegistry::isApplicationPluginMIMEType(mimeType))
111 || (!settings->isJavaEnabled() && MIMETypeRegistry::isJavaAppletMIMEType(mimeType)))
112 return false;
113
114 if (m_frame->document()) {
115 if (m_frame->document()->securityOrigin()->isSandboxed(SandboxPlugins))
116 return false;
117 if (!m_frame->document()->contentSecurityPolicy()->allowObjectFromSource(url))
118 return false;
119 }
120
121 ASSERT(ownerElement->hasTagName(objectTag) || ownerElement->hasTagName(embedTag));
122 return loadPlugin(ownerElement, url, mimeType, paramNames, paramValues, useFallback);
123 }
124
requestObject(HTMLPlugInImageElement * ownerElement,const String & url,const AtomicString & frameName,const String & mimeType,const Vector<String> & paramNames,const Vector<String> & paramValues)125 bool SubframeLoader::requestObject(HTMLPlugInImageElement* ownerElement, const String& url, const AtomicString& frameName, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues)
126 {
127 if (url.isEmpty() && mimeType.isEmpty())
128 return false;
129
130 // FIXME: None of this code should use renderers!
131 RenderEmbeddedObject* renderer = ownerElement->renderEmbeddedObject();
132 ASSERT(renderer);
133 if (!renderer)
134 return false;
135
136 KURL completedURL;
137 if (!url.isEmpty())
138 completedURL = completeURL(url);
139
140 bool useFallback;
141 if (shouldUsePlugin(completedURL, mimeType, ownerElement->shouldPreferPlugInsForImages(), renderer->hasFallbackContent(), useFallback))
142 return requestPlugin(ownerElement, completedURL, mimeType, paramNames, paramValues, useFallback);
143
144 // If the plug-in element already contains a subframe, loadOrRedirectSubframe will re-use it. Otherwise,
145 // it will create a new frame and set it as the RenderPart's widget, causing what was previously
146 // in the widget to be torn down.
147 return loadOrRedirectSubframe(ownerElement, completedURL, frameName, true, true);
148 }
149
150 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
loadMediaPlayerProxyPlugin(Node * node,const KURL & url,const Vector<String> & paramNames,const Vector<String> & paramValues)151 PassRefPtr<Widget> SubframeLoader::loadMediaPlayerProxyPlugin(Node* node, const KURL& url,
152 const Vector<String>& paramNames, const Vector<String>& paramValues)
153 {
154 ASSERT(node->hasTagName(videoTag) || node->hasTagName(audioTag));
155
156 KURL completedURL;
157 if (!url.isEmpty())
158 completedURL = completeURL(url);
159
160 if (!m_frame->document()->securityOrigin()->canDisplay(completedURL)) {
161 FrameLoader::reportLocalLoadFailed(m_frame, completedURL.string());
162 return 0;
163 }
164
165 if (!m_frame->document()->contentSecurityPolicy()->allowMediaFromSource(completedURL))
166 return 0;
167
168 HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(node);
169 RenderPart* renderer = toRenderPart(node->renderer());
170 IntSize size;
171
172 if (renderer)
173 size = IntSize(renderer->contentWidth(), renderer->contentHeight());
174 else if (mediaElement->isVideo())
175 size = RenderVideo::defaultSize();
176
177 if (!m_frame->loader()->checkIfRunInsecureContent(m_frame->document()->securityOrigin(), completedURL))
178 return 0;
179
180 RefPtr<Widget> widget = m_frame->loader()->client()->createMediaPlayerProxyPlugin(size, mediaElement, completedURL,
181 paramNames, paramValues, "application/x-media-element-proxy-plugin");
182
183 if (widget && renderer) {
184 renderer->setWidget(widget);
185 renderer->node()->setNeedsStyleRecalc(SyntheticStyleChange);
186 }
187 m_containsPlugins = true;
188
189 return widget ? widget.release() : 0;
190 }
191 #endif // ENABLE(PLUGIN_PROXY_FOR_VIDEO)
192
createJavaAppletWidget(const IntSize & size,HTMLAppletElement * element,const HashMap<String,String> & args)193 PassRefPtr<Widget> SubframeLoader::createJavaAppletWidget(const IntSize& size, HTMLAppletElement* element, const HashMap<String, String>& args)
194 {
195 String baseURLString;
196 String codeBaseURLString;
197 Vector<String> paramNames;
198 Vector<String> paramValues;
199 HashMap<String, String>::const_iterator end = args.end();
200 for (HashMap<String, String>::const_iterator it = args.begin(); it != end; ++it) {
201 if (equalIgnoringCase(it->first, "baseurl"))
202 baseURLString = it->second;
203 else if (equalIgnoringCase(it->first, "codebase"))
204 codeBaseURLString = it->second;
205 paramNames.append(it->first);
206 paramValues.append(it->second);
207 }
208
209 if (!codeBaseURLString.isEmpty()) {
210 KURL codeBaseURL = completeURL(codeBaseURLString);
211 if (!element->document()->securityOrigin()->canDisplay(codeBaseURL)) {
212 FrameLoader::reportLocalLoadFailed(m_frame, codeBaseURL.string());
213 return 0;
214 }
215
216 if (!element->document()->contentSecurityPolicy()->allowObjectFromSource(codeBaseURL))
217 return 0;
218 }
219
220 if (baseURLString.isEmpty())
221 baseURLString = m_frame->document()->baseURL().string();
222 KURL baseURL = completeURL(baseURLString);
223
224 RefPtr<Widget> widget;
225 if (allowPlugins(AboutToInstantiatePlugin))
226 widget = m_frame->loader()->client()->createJavaAppletWidget(size, element, baseURL, paramNames, paramValues);
227 if (!widget)
228 return 0;
229
230 m_containsPlugins = true;
231 return widget;
232 }
233
loadOrRedirectSubframe(HTMLFrameOwnerElement * ownerElement,const KURL & url,const AtomicString & frameName,bool lockHistory,bool lockBackForwardList)234 Frame* SubframeLoader::loadOrRedirectSubframe(HTMLFrameOwnerElement* ownerElement, const KURL& url, const AtomicString& frameName, bool lockHistory, bool lockBackForwardList)
235 {
236 Frame* frame = ownerElement->contentFrame();
237 if (frame)
238 frame->navigationScheduler()->scheduleLocationChange(m_frame->document()->securityOrigin(), url.string(), m_frame->loader()->outgoingReferrer(), lockHistory, lockBackForwardList);
239 else
240 frame = loadSubframe(ownerElement, url, frameName, m_frame->loader()->outgoingReferrer());
241 return frame;
242 }
243
loadSubframe(HTMLFrameOwnerElement * ownerElement,const KURL & url,const String & name,const String & referrer)244 Frame* SubframeLoader::loadSubframe(HTMLFrameOwnerElement* ownerElement, const KURL& url, const String& name, const String& referrer)
245 {
246 bool allowsScrolling = true;
247 int marginWidth = -1;
248 int marginHeight = -1;
249 if (ownerElement->hasTagName(frameTag) || ownerElement->hasTagName(iframeTag)) {
250 HTMLFrameElementBase* o = static_cast<HTMLFrameElementBase*>(ownerElement);
251 allowsScrolling = o->scrollingMode() != ScrollbarAlwaysOff;
252 marginWidth = o->marginWidth();
253 marginHeight = o->marginHeight();
254 }
255
256 if (!ownerElement->document()->securityOrigin()->canDisplay(url)) {
257 FrameLoader::reportLocalLoadFailed(m_frame, url.string());
258 return 0;
259 }
260
261 if (!ownerElement->document()->contentSecurityPolicy()->allowChildFrameFromSource(url))
262 return 0;
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
allowPlugins(ReasonForCallingAllowPlugins reason)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
shouldUsePlugin(const KURL & url,const String & mimeType,bool shouldPreferPlugInsForImages,bool hasFallback,bool & useFallback)311 bool SubframeLoader::shouldUsePlugin(const KURL& url, const String& mimeType, bool shouldPreferPlugInsForImages, 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, shouldPreferPlugInsForImages);
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
document() const334 Document* SubframeLoader::document() const
335 {
336 return m_frame->document();
337 }
338
loadPlugin(HTMLPlugInImageElement * pluginElement,const KURL & url,const String & mimeType,const Vector<String> & paramNames,const Vector<String> & paramValues,bool useFallback)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 (!document()->securityOrigin()->canDisplay(url)) {
349 FrameLoader::reportLocalLoadFailed(m_frame, url.string());
350 return false;
351 }
352
353 if (!document()->contentSecurityPolicy()->allowObjectFromSource(url))
354 return false;
355
356 FrameLoader* frameLoader = m_frame->loader();
357 if (!frameLoader->checkIfRunInsecureContent(document()->securityOrigin(), url))
358 return false;
359
360 IntSize contentSize(renderer->contentWidth(), renderer->contentHeight());
361 bool loadManually = document()->isPluginDocument() && !m_containsPlugins && toPluginDocument(document())->shouldLoadPluginManually();
362 RefPtr<Widget> widget = frameLoader->client()->createPlugin(contentSize,
363 pluginElement, url, paramNames, paramValues, mimeType, loadManually);
364
365 if (!widget) {
366 renderer->setShowsMissingPluginIndicator();
367 return false;
368 }
369
370 renderer->setWidget(widget);
371 m_containsPlugins = true;
372
373 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) || ENABLE(3D_PLUGIN)
374 pluginElement->setNeedsStyleRecalc(SyntheticStyleChange);
375 #endif
376 return true;
377 }
378
completeURL(const String & url) const379 KURL SubframeLoader::completeURL(const String& url) const
380 {
381 ASSERT(m_frame->document());
382 return m_frame->document()->completeURL(url);
383 }
384
385 } // namespace WebCore
386