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.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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.
34 #include "SubframeLoader.h"
37 #include "ChromeClient.h"
38 #include "ContentSecurityPolicy.h"
39 #include "DiagnosticLoggingKeys.h"
41 #include "FrameLoader.h"
42 #include "FrameLoaderClient.h"
43 #include "HTMLAppletElement.h"
44 #include "HTMLAudioElement.h"
45 #include "HTMLFrameElementBase.h"
46 #include "HTMLNames.h"
47 #include "HTMLObjectElement.h"
48 #include "MIMETypeRegistry.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"
59 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
60 #include "HTMLMediaElement.h"
61 #include "RenderVideo.h"
66 using namespace HTMLNames;
68 SubframeLoader::SubframeLoader(Frame& frame)
69 : m_containsPlugins(false)
74 void SubframeLoader::clear()
76 m_containsPlugins = false;
79 bool SubframeLoader::requestFrame(HTMLFrameOwnerElement& ownerElement, const String& urlString, const AtomicString& frameName, bool lockHistory, bool lockBackForwardList)
81 // Support for <frame src="javascript:string">
84 if (protocolIsJavaScript(urlString)) {
85 scriptURL = completeURL(urlString); // completeURL() encodes the URL.
88 url = completeURL(urlString);
90 Frame* frame = loadOrRedirectSubframe(ownerElement, url, frameName, lockHistory, lockBackForwardList);
94 if (!scriptURL.isEmpty())
95 frame->script().executeIfJavaScriptURL(scriptURL);
100 bool SubframeLoader::resourceWillUsePlugin(const String& url, const String& mimeType, bool shouldPreferPlugInsForImages)
104 completedURL = completeURL(url);
107 return shouldUsePlugin(completedURL, mimeType, shouldPreferPlugInsForImages, false, useFallback);
110 bool SubframeLoader::pluginIsLoadable(HTMLPlugInImageElement& pluginElement, const URL& url, const String& mimeType)
112 if (MIMETypeRegistry::isJavaAppletMIMEType(mimeType)) {
113 if (!m_frame.settings().isJavaEnabled())
115 if (document() && document()->securityOrigin()->isLocal() && !m_frame.settings().isJavaEnabledForLocalFiles())
120 if (document()->isSandboxed(SandboxPlugins))
123 if (!document()->securityOrigin()->canDisplay(url)) {
124 FrameLoader::reportLocalLoadFailed(&m_frame, url.string());
128 String declaredMimeType = document()->isPluginDocument() && document()->ownerElement() ?
129 document()->ownerElement()->fastGetAttribute(HTMLNames::typeAttr) :
130 pluginElement.fastGetAttribute(HTMLNames::typeAttr);
131 if (!document()->contentSecurityPolicy()->allowObjectFromSource(url)
132 || !document()->contentSecurityPolicy()->allowPluginType(mimeType, declaredMimeType, url)) {
133 RenderEmbeddedObject* renderer = pluginElement.renderEmbeddedObject();
134 renderer->setPluginUnavailabilityReason(RenderEmbeddedObject::PluginBlockedByContentSecurityPolicy);
138 if (!m_frame.loader().mixedContentChecker().canRunInsecureContent(document()->securityOrigin(), url))
145 bool SubframeLoader::requestPlugin(HTMLPlugInImageElement& ownerElement, const URL& url, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback)
147 // Application plug-ins are plug-ins implemented by the user agent, for example Qt plug-ins,
148 // as opposed to third-party code such as Flash. The user agent decides whether or not they are
149 // permitted, rather than WebKit.
150 if ((!allowPlugins(AboutToInstantiatePlugin) && !MIMETypeRegistry::isApplicationPluginMIMEType(mimeType)))
153 if (!pluginIsLoadable(ownerElement, url, mimeType))
156 ASSERT(ownerElement.hasTagName(objectTag) || ownerElement.hasTagName(embedTag));
157 return loadPlugin(ownerElement, url, mimeType, paramNames, paramValues, useFallback);
160 static String findPluginMIMETypeFromURL(Page* page, const String& url)
165 size_t dotIndex = url.reverseFind('.');
166 if (dotIndex == notFound)
169 String extension = url.substring(dotIndex + 1);
171 const PluginData& pluginData = page->pluginData();
173 for (size_t i = 0; i < pluginData.mimes().size(); ++i) {
174 const MimeClassInfo& mimeClassInfo = pluginData.mimes()[i];
175 for (size_t j = 0; j < mimeClassInfo.extensions.size(); ++j) {
176 if (equalIgnoringCase(extension, mimeClassInfo.extensions[j]))
177 return mimeClassInfo.type;
184 static void logPluginRequest(Page* page, const String& mimeType, const String& url, bool success)
186 if (!page || !page->settings().diagnosticLoggingEnabled())
189 String newMIMEType = mimeType;
191 // Try to figure out the MIME type from the URL extension.
192 newMIMEType = findPluginMIMETypeFromURL(page, url);
197 String pluginFile = page->pluginData().pluginFileForMimeType(newMIMEType);
198 String description = !pluginFile ? newMIMEType : pluginFile;
200 ChromeClient& chromeClient = page->chrome().client();
201 chromeClient.logDiagnosticMessage(success ? DiagnosticLoggingKeys::pluginLoadedKey() : DiagnosticLoggingKeys::pluginLoadingFailedKey(), description, DiagnosticLoggingKeys::noopKey());
203 if (!page->hasSeenAnyPlugin())
204 chromeClient.logDiagnosticMessage(DiagnosticLoggingKeys::pageContainsAtLeastOnePluginKey(), emptyString(), DiagnosticLoggingKeys::noopKey());
206 if (!page->hasSeenPlugin(description))
207 chromeClient.logDiagnosticMessage(DiagnosticLoggingKeys::pageContainsPluginKey(), description, DiagnosticLoggingKeys::noopKey());
209 page->sawPlugin(description);
212 bool SubframeLoader::requestObject(HTMLPlugInImageElement& ownerElement, const String& url, const AtomicString& frameName, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues)
214 if (url.isEmpty() && mimeType.isEmpty())
219 completedURL = completeURL(url);
221 bool hasFallbackContent = isHTMLObjectElement(ownerElement) && toHTMLObjectElement(ownerElement).hasFallbackContent();
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);
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, true, true);
236 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
237 PassRefPtr<Widget> SubframeLoader::loadMediaPlayerProxyPlugin(HTMLMediaElement& mediaElement, const URL& url, const Vector<String>& paramNames, const Vector<String>& paramValues)
239 ASSERT(mediaElement.hasTagName(videoTag) || isHTMLAudioElement(mediaElement));
243 completedURL = completeURL(url);
245 if (!m_frame.document()->securityOrigin()->canDisplay(completedURL)) {
246 FrameLoader::reportLocalLoadFailed(&m_frame, completedURL.string());
250 if (!m_frame.document()->contentSecurityPolicy()->allowMediaFromSource(completedURL))
253 RenderWidget* renderer = toRenderWidget(mediaElement.renderer());
257 size = roundedIntSize(LayoutSize(renderer->contentWidth(), renderer->contentHeight()));
258 else if (mediaElement.isVideo())
259 size = RenderVideo::defaultSize();
261 if (!m_frame.loader().mixedContentChecker().canRunInsecureContent(m_frame.document()->securityOrigin(), completedURL))
264 RefPtr<Widget> widget = m_frame.loader().client().createMediaPlayerProxyPlugin(size, &mediaElement, completedURL, paramNames, paramValues, "application/x-media-element-proxy-plugin");
266 if (widget && renderer) {
267 renderer->setWidget(widget);
268 renderer->frameOwnerElement().setNeedsStyleRecalc(SyntheticStyleChange);
270 m_containsPlugins = true;
272 return widget ? widget.release() : nullptr;
274 #endif // ENABLE(PLUGIN_PROXY_FOR_VIDEO)
276 PassRefPtr<Widget> SubframeLoader::createJavaAppletWidget(const IntSize& size, HTMLAppletElement& element, const Vector<String>& paramNames, const Vector<String>& paramValues)
278 String baseURLString;
279 String codeBaseURLString;
281 for (size_t i = 0; i < paramNames.size(); ++i) {
282 if (equalIgnoringCase(paramNames[i], "baseurl"))
283 baseURLString = paramValues[i];
284 else if (equalIgnoringCase(paramNames[i], "codebase"))
285 codeBaseURLString = paramValues[i];
288 if (!codeBaseURLString.isEmpty()) {
289 URL codeBaseURL = completeURL(codeBaseURLString);
290 if (!element.document().securityOrigin()->canDisplay(codeBaseURL)) {
291 FrameLoader::reportLocalLoadFailed(&m_frame, codeBaseURL.string());
295 const char javaAppletMimeType[] = "application/x-java-applet";
296 if (!element.document().contentSecurityPolicy()->allowObjectFromSource(codeBaseURL)
297 || !element.document().contentSecurityPolicy()->allowPluginType(javaAppletMimeType, javaAppletMimeType, codeBaseURL))
301 if (baseURLString.isEmpty())
302 baseURLString = m_frame.document()->baseURL().string();
303 URL baseURL = completeURL(baseURLString);
305 RefPtr<Widget> widget;
306 if (allowPlugins(AboutToInstantiatePlugin))
307 widget = m_frame.loader().client().createJavaAppletWidget(size, &element, baseURL, paramNames, paramValues);
309 logPluginRequest(document()->page(), element.serviceType(), String(), widget);
312 RenderEmbeddedObject* renderer = element.renderEmbeddedObject();
314 if (!renderer->isPluginUnavailable())
315 renderer->setPluginUnavailabilityReason(RenderEmbeddedObject::PluginMissing);
319 m_containsPlugins = true;
323 Frame* SubframeLoader::loadOrRedirectSubframe(HTMLFrameOwnerElement& ownerElement, const URL& url, const AtomicString& frameName, bool lockHistory, bool lockBackForwardList)
325 Frame* frame = ownerElement.contentFrame();
327 frame->navigationScheduler().scheduleLocationChange(m_frame.document()->securityOrigin(), url.string(), m_frame.loader().outgoingReferrer(), lockHistory, lockBackForwardList);
329 frame = loadSubframe(ownerElement, url, frameName, m_frame.loader().outgoingReferrer());
334 ASSERT(ownerElement.contentFrame() == frame || !ownerElement.contentFrame());
335 return ownerElement.contentFrame();
338 Frame* SubframeLoader::loadSubframe(HTMLFrameOwnerElement& ownerElement, const URL& url, const String& name, const String& referrer)
340 Ref<Frame> protect(m_frame);
342 bool allowsScrolling = true;
343 int marginWidth = -1;
344 int marginHeight = -1;
345 if (ownerElement.hasTagName(frameTag) || ownerElement.hasTagName(iframeTag)) {
346 HTMLFrameElementBase& frameElementBase = toHTMLFrameElementBase(ownerElement);
347 allowsScrolling = frameElementBase.scrollingMode() != ScrollbarAlwaysOff;
348 marginWidth = frameElementBase.marginWidth();
349 marginHeight = frameElementBase.marginHeight();
352 if (!ownerElement.document().securityOrigin()->canDisplay(url)) {
353 FrameLoader::reportLocalLoadFailed(&m_frame, url.string());
357 if (!SubframeLoadingDisabler::canLoadFrame(ownerElement))
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);
364 m_frame.loader().checkCallImplicitClose();
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();
376 RenderObject* renderer = ownerElement.renderer();
377 FrameView* view = frame->view();
378 if (renderer && renderer->isWidget() && view)
379 toRenderWidget(renderer)->setWidget(view);
381 m_frame.loader().checkCallImplicitClose();
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
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();
398 bool SubframeLoader::allowPlugins(ReasonForCallingAllowPlugins)
400 return m_frame.loader().client().allowPlugins(m_frame.settings().arePluginsEnabled());
403 bool SubframeLoader::shouldUsePlugin(const URL& url, const String& mimeType, bool shouldPreferPlugInsForImages, bool hasFallback, bool& useFallback)
405 if (m_frame.loader().client().shouldAlwaysUsePluginDocument(mimeType)) {
410 // Allow other plug-ins to win over QuickTime because if the user has installed a plug-in that
411 // can handle TIFF (which QuickTime can also handle) they probably intended to override QT.
412 if (m_frame.page() && (mimeType == "image/tiff" || mimeType == "image/tif" || mimeType == "image/x-tiff")) {
413 String pluginName = m_frame.page()->pluginData().pluginNameForMimeType(mimeType);
414 if (!pluginName.isEmpty() && !pluginName.contains("QuickTime", false))
418 ObjectContentType objectType = m_frame.loader().client().objectContentType(url, mimeType, shouldPreferPlugInsForImages);
419 // If an object's content can't be handled and it has no fallback, let
420 // it be handled as a plugin to show the broken plugin icon.
421 useFallback = objectType == ObjectContentNone && hasFallback;
422 return objectType == ObjectContentNone || objectType == ObjectContentNetscapePlugin || objectType == ObjectContentOtherPlugin;
425 Document* SubframeLoader::document() const
427 return m_frame.document();
430 bool SubframeLoader::loadPlugin(HTMLPlugInImageElement& pluginElement, const URL& url, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback)
432 RenderEmbeddedObject* renderer = pluginElement.renderEmbeddedObject();
434 // FIXME: This code should not depend on renderer!
435 if (!renderer || useFallback)
438 pluginElement.subframeLoaderWillCreatePlugIn(url);
440 IntSize contentSize = roundedIntSize(LayoutSize(renderer->contentWidth(), renderer->contentHeight()));
441 bool loadManually = document()->isPluginDocument() && !m_containsPlugins && toPluginDocument(document())->shouldLoadPluginManually();
444 // On iOS, we only tell the plugin to be in full page mode if the containing plugin document is the top level document.
445 if (document()->ownerElement())
446 loadManually = false;
449 RefPtr<Widget> widget = m_frame.loader().client().createPlugin(contentSize, &pluginElement, url, paramNames, paramValues, mimeType, loadManually);
452 if (!renderer->isPluginUnavailable())
453 renderer->setPluginUnavailabilityReason(RenderEmbeddedObject::PluginMissing);
457 pluginElement.subframeLoaderDidCreatePlugIn(widget.get());
458 renderer->setWidget(widget);
459 m_containsPlugins = true;
461 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
462 pluginElement.setNeedsStyleRecalc(SyntheticStyleChange);
467 URL SubframeLoader::completeURL(const String& url) const
469 ASSERT(m_frame.document());
470 return m_frame.document()->completeURL(url);
473 } // namespace WebCore