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