Page::pluginData() should return a reference.
[WebKit-https.git] / Source / WebCore / loader / SubframeLoader.cpp
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 "Chrome.h"
37 #include "ChromeClient.h"
38 #include "ContentSecurityPolicy.h"
39 #include "DiagnosticLoggingKeys.h"
40 #include "Frame.h"
41 #include "FrameLoader.h"
42 #include "FrameLoaderClient.h"
43 #include "HTMLAppletElement.h"
44 #include "HTMLFrameElementBase.h"
45 #include "HTMLNames.h"
46 #include "HTMLPlugInImageElement.h"
47 #include "MIMETypeRegistry.h"
48 #include "Page.h"
49 #include "PluginData.h"
50 #include "PluginDocument.h"
51 #include "RenderEmbeddedObject.h"
52 #include "RenderView.h"
53 #include "ScriptController.h"
54 #include "SecurityOrigin.h"
55 #include "SecurityPolicy.h"
56 #include "Settings.h"
57
58 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
59 #include "HTMLMediaElement.h"
60 #include "RenderVideo.h"
61 #endif
62
63 namespace WebCore {
64     
65 using namespace HTMLNames;
66
67 SubframeLoader::SubframeLoader(Frame* frame)
68     : m_containsPlugins(false)
69     , m_frame(frame)
70 {
71 }
72
73 void SubframeLoader::clear()
74 {
75     m_containsPlugins = false;
76 }
77
78 bool SubframeLoader::requestFrame(HTMLFrameOwnerElement* ownerElement, const String& urlString, const AtomicString& frameName, bool lockHistory, bool lockBackForwardList)
79 {
80     // Support for <frame src="javascript:string">
81     KURL scriptURL;
82     KURL url;
83     if (protocolIsJavaScript(urlString)) {
84         scriptURL = completeURL(urlString); // completeURL() encodes the URL.
85         url = blankURL();
86     } else
87         url = completeURL(urlString);
88
89     Frame* frame = loadOrRedirectSubframe(ownerElement, url, frameName, lockHistory, lockBackForwardList);
90     if (!frame)
91         return false;
92
93     if (!scriptURL.isEmpty())
94         frame->script().executeIfJavaScriptURL(scriptURL);
95
96     return true;
97 }
98     
99 bool SubframeLoader::resourceWillUsePlugin(const String& url, const String& mimeType, bool shouldPreferPlugInsForImages)
100 {
101     KURL completedURL;
102     if (!url.isEmpty())
103         completedURL = completeURL(url);
104
105     bool useFallback;
106     return shouldUsePlugin(completedURL, mimeType, shouldPreferPlugInsForImages, false, useFallback);
107 }
108
109 bool SubframeLoader::pluginIsLoadable(HTMLPlugInImageElement* pluginElement, const KURL& url, const String& mimeType)
110 {
111     if (MIMETypeRegistry::isJavaAppletMIMEType(mimeType)) {
112         if (!m_frame->settings().isJavaEnabled())
113             return false;
114         if (document() && document()->securityOrigin()->isLocal() && !m_frame->settings().isJavaEnabledForLocalFiles())
115             return false;
116     }
117
118     if (document()) {
119         if (document()->isSandboxed(SandboxPlugins))
120             return false;
121
122         if (!document()->securityOrigin()->canDisplay(url)) {
123             FrameLoader::reportLocalLoadFailed(m_frame, url.string());
124             return false;
125         }
126
127         String declaredMimeType = document()->isPluginDocument() && document()->ownerElement() ?
128             document()->ownerElement()->fastGetAttribute(HTMLNames::typeAttr) :
129             pluginElement->fastGetAttribute(HTMLNames::typeAttr);
130         if (!document()->contentSecurityPolicy()->allowObjectFromSource(url)
131             || !document()->contentSecurityPolicy()->allowPluginType(mimeType, declaredMimeType, url)) {
132             RenderEmbeddedObject* renderer = pluginElement->renderEmbeddedObject();
133             renderer->setPluginUnavailabilityReason(RenderEmbeddedObject::PluginBlockedByContentSecurityPolicy);
134             return false;
135         }
136
137         if (!m_frame->loader().mixedContentChecker()->canRunInsecureContent(document()->securityOrigin(), url))
138             return false;
139     }
140
141     return true;
142 }
143
144 bool SubframeLoader::requestPlugin(HTMLPlugInImageElement* ownerElement, const KURL& url, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback)
145 {
146     // Application plug-ins are plug-ins implemented by the user agent, for example Qt plug-ins,
147     // as opposed to third-party code such as Flash. The user agent decides whether or not they are
148     // permitted, rather than WebKit.
149     if ((!allowPlugins(AboutToInstantiatePlugin) && !MIMETypeRegistry::isApplicationPluginMIMEType(mimeType)))
150         return false;
151
152     if (!pluginIsLoadable(ownerElement, url, mimeType))
153         return false;
154
155     ASSERT(ownerElement->hasTagName(objectTag) || ownerElement->hasTagName(embedTag));
156     return loadPlugin(ownerElement, url, mimeType, paramNames, paramValues, useFallback);
157 }
158
159 static String findPluginMIMETypeFromURL(Page* page, const String& url)
160 {
161     if (!url)
162         return String();
163
164     size_t dotIndex = url.reverseFind('.');
165     if (dotIndex == notFound)
166         return String();
167
168     String extension = url.substring(dotIndex + 1);
169
170     const PluginData& pluginData = page->pluginData();
171
172     for (size_t i = 0; i < pluginData.mimes().size(); ++i) {
173         const MimeClassInfo& mimeClassInfo = pluginData.mimes()[i];
174         for (size_t j = 0; j < mimeClassInfo.extensions.size(); ++j) {
175             if (equalIgnoringCase(extension, mimeClassInfo.extensions[j]))
176                 return mimeClassInfo.type;
177         }
178     }
179
180     return String();
181 }
182
183 static void logPluginRequest(Page* page, const String& mimeType, const String& url, bool success)
184 {
185     if (!page || !page->settings().diagnosticLoggingEnabled())
186         return;
187
188     String newMIMEType = mimeType;
189     if (!newMIMEType) {
190         // Try to figure out the MIME type from the URL extension.
191         newMIMEType = findPluginMIMETypeFromURL(page, url);
192         if (!newMIMEType)
193             return;
194     }
195
196     String pluginFile = page->pluginData().pluginFileForMimeType(newMIMEType);
197     String description = !pluginFile ? newMIMEType : pluginFile;
198
199     ChromeClient& chromeClient = page->chrome().client();
200     chromeClient.logDiagnosticMessage(success ? DiagnosticLoggingKeys::pluginLoadedKey() : DiagnosticLoggingKeys::pluginLoadingFailedKey(), description, DiagnosticLoggingKeys::noopKey());
201
202     if (!page->hasSeenAnyPlugin())
203         chromeClient.logDiagnosticMessage(DiagnosticLoggingKeys::pageContainsAtLeastOnePluginKey(), emptyString(), DiagnosticLoggingKeys::noopKey());
204     
205     if (!page->hasSeenPlugin(description))
206         chromeClient.logDiagnosticMessage(DiagnosticLoggingKeys::pageContainsPluginKey(), description, DiagnosticLoggingKeys::noopKey());
207
208     page->sawPlugin(description);
209 }
210
211 bool SubframeLoader::requestObject(HTMLPlugInImageElement* ownerElement, const String& url, const AtomicString& frameName, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues)
212 {
213     if (url.isEmpty() && mimeType.isEmpty())
214         return false;
215
216     // FIXME: None of this code should use renderers!
217     RenderEmbeddedObject* renderer = ownerElement->renderEmbeddedObject();
218     ASSERT(renderer);
219     if (!renderer)
220         return false;
221
222     KURL completedURL;
223     if (!url.isEmpty())
224         completedURL = completeURL(url);
225
226     bool useFallback;
227     if (shouldUsePlugin(completedURL, mimeType, ownerElement->shouldPreferPlugInsForImages(), renderer->hasFallbackContent(), useFallback)) {
228         bool success = requestPlugin(ownerElement, completedURL, mimeType, paramNames, paramValues, useFallback);
229         logPluginRequest(document()->page(), mimeType, completedURL, success);
230         return success;
231     }
232
233     // If the plug-in element already contains a subframe, loadOrRedirectSubframe will re-use it. Otherwise,
234     // it will create a new frame and set it as the RenderPart's widget, causing what was previously 
235     // in the widget to be torn down.
236     return loadOrRedirectSubframe(ownerElement, completedURL, frameName, true, true);
237 }
238
239 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
240 PassRefPtr<Widget> SubframeLoader::loadMediaPlayerProxyPlugin(Node* node, const KURL& url,
241     const Vector<String>& paramNames, const Vector<String>& paramValues)
242 {
243     ASSERT(node->hasTagName(videoTag) || isHTMLAudioElement(node));
244
245     KURL completedURL;
246     if (!url.isEmpty())
247         completedURL = completeURL(url);
248
249     if (!m_frame->document()->securityOrigin()->canDisplay(completedURL)) {
250         FrameLoader::reportLocalLoadFailed(m_frame, completedURL.string());
251         return 0;
252     }
253
254     if (!m_frame->document()->contentSecurityPolicy()->allowMediaFromSource(completedURL))
255         return 0;
256
257     HTMLMediaElement* mediaElement = toHTMLMediaElement(node);
258     RenderPart* renderer = toRenderPart(node->renderer());
259     IntSize size;
260
261     if (renderer)
262         size = roundedIntSize(LayoutSize(renderer->contentWidth(), renderer->contentHeight()));
263     else if (mediaElement->isVideo())
264         size = RenderVideo::defaultSize();
265
266     if (!m_frame->loader().mixedContentChecker()->canRunInsecureContent(m_frame->document()->securityOrigin(), completedURL))
267         return 0;
268
269     RefPtr<Widget> widget = m_frame->loader().client().createMediaPlayerProxyPlugin(size, mediaElement, completedURL,
270                                          paramNames, paramValues, "application/x-media-element-proxy-plugin");
271
272     if (widget && renderer) {
273         renderer->setWidget(widget);
274         renderer->node()->setNeedsStyleRecalc(SyntheticStyleChange);
275     }
276     m_containsPlugins = true;
277
278     return widget ? widget.release() : 0;
279 }
280 #endif // ENABLE(PLUGIN_PROXY_FOR_VIDEO)
281
282 PassRefPtr<Widget> SubframeLoader::createJavaAppletWidget(const IntSize& size, HTMLAppletElement* element, const Vector<String>& paramNames, const Vector<String>& paramValues)
283 {
284     String baseURLString;
285     String codeBaseURLString;
286
287     for (size_t i = 0; i < paramNames.size(); ++i) {
288         if (equalIgnoringCase(paramNames[i], "baseurl"))
289             baseURLString = paramValues[i];
290         else if (equalIgnoringCase(paramNames[i], "codebase"))
291             codeBaseURLString = paramValues[i];
292     }
293
294     if (!codeBaseURLString.isEmpty()) {
295         KURL codeBaseURL = completeURL(codeBaseURLString);
296         if (!element->document()->securityOrigin()->canDisplay(codeBaseURL)) {
297             FrameLoader::reportLocalLoadFailed(m_frame, codeBaseURL.string());
298             return 0;
299         }
300
301         const char javaAppletMimeType[] = "application/x-java-applet";
302         if (!element->document()->contentSecurityPolicy()->allowObjectFromSource(codeBaseURL)
303             || !element->document()->contentSecurityPolicy()->allowPluginType(javaAppletMimeType, javaAppletMimeType, codeBaseURL))
304             return 0;
305     }
306
307     if (baseURLString.isEmpty())
308         baseURLString = m_frame->document()->baseURL().string();
309     KURL baseURL = completeURL(baseURLString);
310
311     RefPtr<Widget> widget;
312     if (allowPlugins(AboutToInstantiatePlugin))
313         widget = m_frame->loader().client().createJavaAppletWidget(size, element, baseURL, paramNames, paramValues);
314
315     logPluginRequest(document()->page(), element->serviceType(), String(), widget);
316
317     if (!widget) {
318         RenderEmbeddedObject* renderer = element->renderEmbeddedObject();
319
320         if (!renderer->isPluginUnavailable())
321             renderer->setPluginUnavailabilityReason(RenderEmbeddedObject::PluginMissing);
322         return 0;
323     }
324
325     m_containsPlugins = true;
326     return widget;
327 }
328
329 Frame* SubframeLoader::loadOrRedirectSubframe(HTMLFrameOwnerElement* ownerElement, const KURL& url, const AtomicString& frameName, bool lockHistory, bool lockBackForwardList)
330 {
331     Frame* frame = ownerElement->contentFrame();
332     if (frame)
333         frame->navigationScheduler().scheduleLocationChange(m_frame->document()->securityOrigin(), url.string(), m_frame->loader().outgoingReferrer(), lockHistory, lockBackForwardList);
334     else
335         frame = loadSubframe(ownerElement, url, frameName, m_frame->loader().outgoingReferrer());
336
337     ASSERT(ownerElement->contentFrame() == frame || !ownerElement->contentFrame());
338     return ownerElement->contentFrame();
339 }
340
341 Frame* SubframeLoader::loadSubframe(HTMLFrameOwnerElement* ownerElement, const KURL& url, const String& name, const String& referrer)
342 {
343     RefPtr<Frame> protect(m_frame);
344
345     bool allowsScrolling = true;
346     int marginWidth = -1;
347     int marginHeight = -1;
348     if (ownerElement->hasTagName(frameTag) || ownerElement->hasTagName(iframeTag)) {
349         HTMLFrameElementBase* frameElementBase = toHTMLFrameElementBase(ownerElement);
350         allowsScrolling = frameElementBase->scrollingMode() != ScrollbarAlwaysOff;
351         marginWidth = frameElementBase->marginWidth();
352         marginHeight = frameElementBase->marginHeight();
353     }
354
355     if (!ownerElement->document()->securityOrigin()->canDisplay(url)) {
356         FrameLoader::reportLocalLoadFailed(m_frame, url.string());
357         return 0;
358     }
359
360     String referrerToUse = SecurityPolicy::generateReferrerHeader(ownerElement->document()->referrerPolicy(), url, referrer);
361     RefPtr<Frame> frame = m_frame->loader().client().createFrame(url, name, ownerElement, referrerToUse, allowsScrolling, marginWidth, marginHeight);
362
363     if (!frame)  {
364         m_frame->loader().checkCallImplicitClose();
365         return 0;
366     }
367     
368     // All new frames will have m_isComplete set to true at this point due to synchronously loading
369     // an empty document in FrameLoader::init(). But many frames will now be starting an
370     // asynchronous load of url, so we set m_isComplete to false and then check if the load is
371     // actually completed below. (Note that we set m_isComplete to false even for synchronous
372     // loads, so that checkCompleted() below won't bail early.)
373     // FIXME: Can we remove this entirely? m_isComplete normally gets set to false when a load is committed.
374     frame->loader().started();
375    
376     RenderObject* renderer = ownerElement->renderer();
377     FrameView* view = frame->view();
378     if (renderer && renderer->isWidget() && view)
379         toRenderWidget(renderer)->setWidget(view);
380     
381     m_frame->loader().checkCallImplicitClose();
382     
383     // Some loads are performed synchronously (e.g., about:blank and loads
384     // cancelled by returning a null ResourceRequest from requestFromDelegate).
385     // In these cases, the synchronous load would have finished
386     // before we could connect the signals, so make sure to send the 
387     // completed() signal for the child by hand and mark the load as being
388     // complete.
389     // FIXME: In this case the Frame will have finished loading before 
390     // it's being added to the child list. It would be a good idea to
391     // create the child first, then invoke the loader separately.
392     if (frame->loader().state() == FrameStateComplete && !frame->loader().policyDocumentLoader())
393         frame->loader().checkCompleted();
394
395     return frame.get();
396 }
397
398 bool SubframeLoader::allowPlugins(ReasonForCallingAllowPlugins reason)
399 {
400     bool allowed = m_frame->loader().client().allowPlugins(m_frame->settings().arePluginsEnabled());
401     if (!allowed && reason == AboutToInstantiatePlugin)
402         m_frame->loader().client().didNotAllowPlugins();
403     return allowed;
404 }
405
406 bool SubframeLoader::shouldUsePlugin(const KURL& url, const String& mimeType, bool shouldPreferPlugInsForImages, bool hasFallback, bool& useFallback)
407 {
408     if (m_frame->loader().client().shouldAlwaysUsePluginDocument(mimeType)) {
409         useFallback = false;
410         return true;
411     }
412
413     // Allow other plug-ins to win over QuickTime because if the user has installed a plug-in that
414     // can handle TIFF (which QuickTime can also handle) they probably intended to override QT.
415     if (m_frame->page() && (mimeType == "image/tiff" || mimeType == "image/tif" || mimeType == "image/x-tiff")) {
416         String pluginName = m_frame->page()->pluginData().pluginNameForMimeType(mimeType);
417         if (!pluginName.isEmpty() && !pluginName.contains("QuickTime", false)) 
418             return true;
419     }
420         
421     ObjectContentType objectType = m_frame->loader().client().objectContentType(url, mimeType, shouldPreferPlugInsForImages);
422     // If an object's content can't be handled and it has no fallback, let
423     // it be handled as a plugin to show the broken plugin icon.
424     useFallback = objectType == ObjectContentNone && hasFallback;
425     return objectType == ObjectContentNone || objectType == ObjectContentNetscapePlugin || objectType == ObjectContentOtherPlugin;
426 }
427
428 Document* SubframeLoader::document() const
429 {
430     return m_frame->document();
431 }
432
433 bool SubframeLoader::loadPlugin(HTMLPlugInImageElement* pluginElement, const KURL& url, const String& mimeType,
434     const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback)
435 {
436     RenderEmbeddedObject* renderer = pluginElement->renderEmbeddedObject();
437
438     // FIXME: This code should not depend on renderer!
439     if (!renderer || useFallback)
440         return false;
441
442     pluginElement->subframeLoaderWillCreatePlugIn(url);
443
444     IntSize contentSize = roundedIntSize(LayoutSize(renderer->contentWidth(), renderer->contentHeight()));
445     bool loadManually = document()->isPluginDocument() && !m_containsPlugins && toPluginDocument(document())->shouldLoadPluginManually();
446     RefPtr<Widget> widget = m_frame->loader().client().createPlugin(contentSize,
447         pluginElement, url, paramNames, paramValues, mimeType, loadManually);
448
449     if (!widget) {
450         if (!renderer->isPluginUnavailable())
451             renderer->setPluginUnavailabilityReason(RenderEmbeddedObject::PluginMissing);
452         return false;
453     }
454
455     pluginElement->subframeLoaderDidCreatePlugIn(widget.get());
456     renderer->setWidget(widget);
457     m_containsPlugins = true;
458  
459 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
460     pluginElement->setNeedsStyleRecalc(SyntheticStyleChange);
461 #endif
462     return true;
463 }
464
465 KURL SubframeLoader::completeURL(const String& url) const
466 {
467     ASSERT(m_frame->document());
468     return m_frame->document()->completeURL(url);
469 }
470
471 } // namespace WebCore