• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
56 namespace WebCore {
57 
58 using namespace HTMLNames;
59 
SubframeLoader(Frame * frame)60 SubframeLoader::SubframeLoader(Frame* frame)
61     : m_containsPlugins(false)
62     , m_frame(frame)
63 {
64 }
65 
clear()66 void SubframeLoader::clear()
67 {
68     m_containsPlugins = false;
69 }
70 
requestFrame(HTMLFrameOwnerElement * ownerElement,const String & urlString,const AtomicString & frameName,bool lockHistory,bool lockBackForwardList)71 bool 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 
resourceWillUsePlugin(const String & url,const String & mimeType,bool shouldPreferPlugInsForImages)92 bool 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 
requestPlugin(HTMLPlugInImageElement * ownerElement,const KURL & url,const String & mimeType,const Vector<String> & paramNames,const Vector<String> & paramValues,bool useFallback)102 bool 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 
requestObject(HTMLPlugInImageElement * ownerElement,const String & url,const AtomicString & frameName,const String & mimeType,const Vector<String> & paramNames,const Vector<String> & paramValues)124 bool 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)
loadMediaPlayerProxyPlugin(Node * node,const KURL & url,const Vector<String> & paramNames,const Vector<String> & paramValues)150 PassRefPtr<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 
createJavaAppletWidget(const IntSize & size,HTMLAppletElement * element,const HashMap<String,String> & args)188 PassRefPtr<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 
loadOrRedirectSubframe(HTMLFrameOwnerElement * ownerElement,const KURL & url,const AtomicString & frameName,bool lockHistory,bool lockBackForwardList)226 Frame* 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 
loadSubframe(HTMLFrameOwnerElement * ownerElement,const KURL & url,const String & name,const String & referrer)236 Frame* 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 
allowPlugins(ReasonForCallingAllowPlugins reason)291 bool 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 
shouldUsePlugin(const KURL & url,const String & mimeType,bool shouldPreferPlugInsForImages,bool hasFallback,bool & useFallback)300 bool 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 
document() const323 Document* SubframeLoader::document() const
324 {
325     return m_frame->document();
326 }
327 
loadPlugin(HTMLPlugInImageElement * pluginElement,const KURL & url,const String & mimeType,const Vector<String> & paramNames,const Vector<String> & paramValues,bool useFallback)328 bool 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 
completeURL(const String & url) const364 KURL 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