Move URL from WebCore to WTF
[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 "DocumentLoader.h"
40 #include "Frame.h"
41 #include "FrameLoader.h"
42 #include "FrameLoaderClient.h"
43 #include "HTMLAppletElement.h"
44 #include "HTMLFrameElement.h"
45 #include "HTMLIFrameElement.h"
46 #include "HTMLNames.h"
47 #include "HTMLObjectElement.h"
48 #include "MIMETypeRegistry.h"
49 #include "NavigationScheduler.h"
50 #include "Page.h"
51 #include "PluginData.h"
52 #include "PluginDocument.h"
53 #include "RenderEmbeddedObject.h"
54 #include "RenderView.h"
55 #include "ScriptController.h"
56 #include "SecurityOrigin.h"
57 #include "SecurityPolicy.h"
58 #include "Settings.h"
59
60 namespace WebCore {
61     
62 using namespace HTMLNames;
63
64 SubframeLoader::SubframeLoader(Frame& frame)
65     : m_containsPlugins(false)
66     , m_frame(frame)
67 {
68 }
69
70 void SubframeLoader::clear()
71 {
72     m_containsPlugins = false;
73 }
74
75 bool SubframeLoader::requestFrame(HTMLFrameOwnerElement& ownerElement, const String& urlString, const AtomicString& frameName, LockHistory lockHistory, LockBackForwardList lockBackForwardList)
76 {
77     // Support for <frame src="javascript:string">
78     URL scriptURL;
79     URL url;
80     if (WTF::protocolIsJavaScript(urlString)) {
81         scriptURL = completeURL(urlString); // completeURL() encodes the URL.
82         url = WTF::blankURL();
83     } else
84         url = completeURL(urlString);
85
86     if (shouldConvertInvalidURLsToBlank() && !url.isValid())
87         url = WTF::blankURL();
88
89     bool hasExistingFrame = ownerElement.contentFrame();
90     Frame* frame = loadOrRedirectSubframe(ownerElement, url, frameName, lockHistory, lockBackForwardList);
91     if (!frame)
92         return false;
93
94     // If we create a new subframe then an empty document is loaded into it synchronously and may
95     // cause script execution (say, via a DOM load event handler) that can do anything, including
96     // navigating the subframe. We only want to evaluate scriptURL if the frame has not been navigated.
97     bool canExecuteScript = hasExistingFrame || (frame->loader().documentLoader() && frame->loader().documentLoader()->originalURL() == WTF::blankURL());
98     if (!scriptURL.isEmpty() && canExecuteScript && ownerElement.isURLAllowed(scriptURL))
99         frame->script().executeIfJavaScriptURL(scriptURL);
100
101     return true;
102 }
103     
104 bool SubframeLoader::resourceWillUsePlugin(const String& url, const String& mimeType)
105 {
106     URL completedURL;
107     if (!url.isEmpty())
108         completedURL = completeURL(url);
109
110     bool useFallback;
111     return shouldUsePlugin(completedURL, mimeType, false, useFallback);
112 }
113
114 bool SubframeLoader::pluginIsLoadable(const URL& url, const String& mimeType)
115 {
116     auto* document = m_frame.document();
117
118     if (MIMETypeRegistry::isJavaAppletMIMEType(mimeType)) {
119         if (!m_frame.settings().isJavaEnabled())
120             return false;
121         if (document && document->securityOrigin().isLocal() && !m_frame.settings().isJavaEnabledForLocalFiles())
122             return false;
123     }
124
125     if (document) {
126         if (document->isSandboxed(SandboxPlugins))
127             return false;
128
129         if (!document->securityOrigin().canDisplay(url)) {
130             FrameLoader::reportLocalLoadFailed(&m_frame, url.string());
131             return false;
132         }
133
134         if (!m_frame.loader().mixedContentChecker().canRunInsecureContent(document->securityOrigin(), url))
135             return false;
136     }
137
138     return true;
139 }
140
141 bool SubframeLoader::requestPlugin(HTMLPlugInImageElement& ownerElement, const URL& url, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback)
142 {
143     // Application plug-ins are plug-ins implemented by the user agent, for example Qt plug-ins,
144     // as opposed to third-party code such as Flash. The user agent decides whether or not they are
145     // permitted, rather than WebKit.
146     if ((!allowPlugins() && !MIMETypeRegistry::isApplicationPluginMIMEType(mimeType)))
147         return false;
148
149     if (!pluginIsLoadable(url, mimeType))
150         return false;
151
152     ASSERT(ownerElement.hasTagName(objectTag) || ownerElement.hasTagName(embedTag));
153     return loadPlugin(ownerElement, url, mimeType, paramNames, paramValues, useFallback);
154 }
155
156 static String findPluginMIMETypeFromURL(Page* page, const String& url)
157 {
158     if (!url)
159         return String();
160
161     size_t dotIndex = url.reverseFind('.');
162     if (dotIndex == notFound)
163         return String();
164
165     String extension = url.substring(dotIndex + 1);
166
167     const PluginData& pluginData = page->pluginData();
168
169     Vector<MimeClassInfo> mimes;
170     Vector<size_t> mimePluginIndices;
171     pluginData.getWebVisibleMimesAndPluginIndices(mimes, mimePluginIndices);
172     for (auto& mime : mimes) {
173         for (auto& mimeExtension : mime.extensions) {
174             if (equalIgnoringASCIICase(extension, mimeExtension))
175                 return mime.type;
176         }
177     }
178
179     return String();
180 }
181
182 static void logPluginRequest(Page* page, const String& mimeType, const String& url, bool success)
183 {
184     if (!page)
185         return;
186
187     String newMIMEType = mimeType;
188     if (!newMIMEType) {
189         // Try to figure out the MIME type from the URL extension.
190         newMIMEType = findPluginMIMETypeFromURL(page, url);
191         if (!newMIMEType)
192             return;
193     }
194
195     String pluginFile = page->pluginData().pluginFileForWebVisibleMimeType(newMIMEType);
196     String description = !pluginFile ? newMIMEType : pluginFile;
197
198     DiagnosticLoggingClient& diagnosticLoggingClient = page->diagnosticLoggingClient();
199     diagnosticLoggingClient.logDiagnosticMessage(success ? DiagnosticLoggingKeys::pluginLoadedKey() : DiagnosticLoggingKeys::pluginLoadingFailedKey(), description, ShouldSample::No);
200
201     if (!page->hasSeenAnyPlugin())
202         diagnosticLoggingClient.logDiagnosticMessage(DiagnosticLoggingKeys::pageContainsAtLeastOnePluginKey(), emptyString(), ShouldSample::No);
203
204     if (!page->hasSeenPlugin(description))
205         diagnosticLoggingClient.logDiagnosticMessage(DiagnosticLoggingKeys::pageContainsPluginKey(), description, ShouldSample::No);
206
207     page->sawPlugin(description);
208 }
209
210 bool SubframeLoader::requestObject(HTMLPlugInImageElement& ownerElement, const String& url, const AtomicString& frameName, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues)
211 {
212     if (url.isEmpty() && mimeType.isEmpty())
213         return false;
214
215     auto& document = ownerElement.document();
216
217     URL completedURL;
218     if (!url.isEmpty())
219         completedURL = completeURL(url);
220
221     document.contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(completedURL, ContentSecurityPolicy::InsecureRequestType::Load);
222
223     bool hasFallbackContent = is<HTMLObjectElement>(ownerElement) && downcast<HTMLObjectElement>(ownerElement).hasFallbackContent();
224
225     bool useFallback;
226     if (shouldUsePlugin(completedURL, mimeType, hasFallbackContent, useFallback)) {
227         bool success = requestPlugin(ownerElement, completedURL, mimeType, paramNames, paramValues, useFallback);
228         logPluginRequest(document.page(), mimeType, completedURL, success);
229         return success;
230     }
231
232     // If the plug-in element already contains a subframe, loadOrRedirectSubframe will re-use it. Otherwise,
233     // it will create a new frame and set it as the RenderWidget's Widget, causing what was previously 
234     // in the widget to be torn down.
235     return loadOrRedirectSubframe(ownerElement, completedURL, frameName, LockHistory::Yes, LockBackForwardList::Yes);
236 }
237
238 RefPtr<Widget> SubframeLoader::createJavaAppletWidget(const IntSize& size, HTMLAppletElement& element, const Vector<String>& paramNames, const Vector<String>& paramValues)
239 {
240     String baseURLString;
241     String codeBaseURLString;
242
243     for (size_t i = 0; i < paramNames.size(); ++i) {
244         if (equalLettersIgnoringASCIICase(paramNames[i], "baseurl"))
245             baseURLString = paramValues[i];
246         else if (equalLettersIgnoringASCIICase(paramNames[i], "codebase"))
247             codeBaseURLString = paramValues[i];
248     }
249
250     if (!codeBaseURLString.isEmpty()) {
251         URL codeBaseURL = completeURL(codeBaseURLString);
252         if (!element.document().securityOrigin().canDisplay(codeBaseURL)) {
253             FrameLoader::reportLocalLoadFailed(&m_frame, codeBaseURL.string());
254             return nullptr;
255         }
256
257         const char javaAppletMimeType[] = "application/x-java-applet";
258         ASSERT(element.document().contentSecurityPolicy());
259         auto& contentSecurityPolicy = *element.document().contentSecurityPolicy();
260         // Elements in user agent show tree should load whatever the embedding document policy is.
261         if (!element.isInUserAgentShadowTree()
262             && (!contentSecurityPolicy.allowObjectFromSource(codeBaseURL) || !contentSecurityPolicy.allowPluginType(javaAppletMimeType, javaAppletMimeType, codeBaseURL)))
263             return nullptr;
264     }
265
266     if (baseURLString.isEmpty())
267         baseURLString = element.document().baseURL().string();
268     URL baseURL = completeURL(baseURLString);
269
270     RefPtr<Widget> widget;
271     if (allowPlugins())
272         widget = m_frame.loader().client().createJavaAppletWidget(size, element, baseURL, paramNames, paramValues);
273
274     logPluginRequest(m_frame.page(), element.serviceType(), String(), widget);
275
276     if (!widget) {
277         RenderEmbeddedObject* renderer = element.renderEmbeddedObject();
278
279         if (!renderer->isPluginUnavailable())
280             renderer->setPluginUnavailabilityReason(RenderEmbeddedObject::PluginMissing);
281         return nullptr;
282     }
283
284     m_containsPlugins = true;
285     return widget;
286 }
287
288 Frame* SubframeLoader::loadOrRedirectSubframe(HTMLFrameOwnerElement& ownerElement, const URL& requestURL, const AtomicString& frameName, LockHistory lockHistory, LockBackForwardList lockBackForwardList)
289 {
290     auto& initiatingDocument = ownerElement.document();
291
292     URL upgradedRequestURL = requestURL;
293     initiatingDocument.contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(upgradedRequestURL, ContentSecurityPolicy::InsecureRequestType::Load);
294
295     auto* frame = ownerElement.contentFrame();
296     if (frame)
297         frame->navigationScheduler().scheduleLocationChange(initiatingDocument, initiatingDocument.securityOrigin(), upgradedRequestURL, m_frame.loader().outgoingReferrer(), lockHistory, lockBackForwardList);
298     else
299         frame = loadSubframe(ownerElement, upgradedRequestURL, frameName, m_frame.loader().outgoingReferrer());
300
301     if (!frame)
302         return nullptr;
303
304     ASSERT(ownerElement.contentFrame() == frame || !ownerElement.contentFrame());
305     return ownerElement.contentFrame();
306 }
307
308 Frame* SubframeLoader::loadSubframe(HTMLFrameOwnerElement& ownerElement, const URL& url, const String& name, const String& referrer)
309 {
310     Ref<Frame> protect(m_frame);
311     auto document = makeRef(ownerElement.document());
312
313     if (!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(document->referrerPolicy(), url, referrer);
322
323     // Prevent initial empty document load from triggering load events.
324     document->incrementLoadEventDelayCount();
325
326     auto frame = m_frame.loader().client().createFrame(url, name, ownerElement, referrerToUse);
327
328     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     auto* 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 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);
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 == ObjectContentType::None && hasFallback;
381
382     return objectType == ObjectContentType::None || objectType == ObjectContentType::PlugIn;
383 }
384
385 bool SubframeLoader::loadPlugin(HTMLPlugInImageElement& pluginElement, const URL& url, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback)
386 {
387     if (useFallback)
388         return false;
389
390     auto& document = pluginElement.document();
391     auto* renderer = pluginElement.renderEmbeddedObject();
392
393     // FIXME: This code should not depend on renderer!
394     if (!renderer)
395         return false;
396
397     pluginElement.subframeLoaderWillCreatePlugIn(url);
398
399     IntSize contentSize = roundedIntSize(LayoutSize(renderer->contentWidth(), renderer->contentHeight()));
400     bool loadManually = is<PluginDocument>(document) && !m_containsPlugins && downcast<PluginDocument>(document).shouldLoadPluginManually();
401
402 #if PLATFORM(IOS_FAMILY)
403     // On iOS, we only tell the plugin to be in full page mode if the containing plugin document is the top level document.
404     if (document.ownerElement())
405         loadManually = false;
406 #endif
407
408     auto weakRenderer = makeWeakPtr(*renderer);
409
410     auto widget = m_frame.loader().client().createPlugin(contentSize, pluginElement, url, paramNames, paramValues, mimeType, loadManually);
411
412     // The call to createPlugin *may* cause this renderer to disappear from underneath.
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(WTFMove(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 bool SubframeLoader::shouldConvertInvalidURLsToBlank() const
435 {
436     return m_frame.settings().shouldConvertInvalidURLsToBlank();
437 }
438
439 } // namespace WebCore