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