| /* |
| * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. |
| * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) |
| * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) |
| * Copyright (C) 2008 Alp Toker <alp@atoker.com> |
| * Copyright (C) Research In Motion Limited 2009. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of |
| * its contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
| * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "SubframeLoader.h" |
| |
| #include "ContentSecurityPolicy.h" |
| #include "Frame.h" |
| #include "FrameLoaderClient.h" |
| #include "HTMLAppletElement.h" |
| #include "HTMLFrameElementBase.h" |
| #include "HTMLNames.h" |
| #include "HTMLPlugInImageElement.h" |
| #include "MIMETypeRegistry.h" |
| #include "Page.h" |
| #include "PluginData.h" |
| #include "PluginDocument.h" |
| #include "RenderEmbeddedObject.h" |
| #include "RenderView.h" |
| #include "Settings.h" |
| |
| #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) |
| #include "HTMLMediaElement.h" |
| #include "RenderVideo.h" |
| #endif |
| |
| namespace WebCore { |
| |
| using namespace HTMLNames; |
| |
| SubframeLoader::SubframeLoader(Frame* frame) |
| : m_containsPlugins(false) |
| , m_frame(frame) |
| { |
| } |
| |
| void SubframeLoader::clear() |
| { |
| m_containsPlugins = false; |
| } |
| |
| bool SubframeLoader::requestFrame(HTMLFrameOwnerElement* ownerElement, const String& urlString, const AtomicString& frameName, bool lockHistory, bool lockBackForwardList) |
| { |
| // Support for <frame src="javascript:string"> |
| KURL scriptURL; |
| KURL url; |
| if (protocolIsJavaScript(urlString)) { |
| scriptURL = completeURL(urlString); // completeURL() encodes the URL. |
| url = blankURL(); |
| } else |
| url = completeURL(urlString); |
| |
| Frame* frame = loadOrRedirectSubframe(ownerElement, url, frameName, lockHistory, lockBackForwardList); |
| if (!frame) |
| return false; |
| |
| if (!scriptURL.isEmpty()) |
| frame->script()->executeIfJavaScriptURL(scriptURL); |
| |
| return true; |
| } |
| |
| bool SubframeLoader::resourceWillUsePlugin(const String& url, const String& mimeType, bool shouldPreferPlugInsForImages) |
| { |
| KURL completedURL; |
| if (!url.isEmpty()) |
| completedURL = completeURL(url); |
| |
| bool useFallback; |
| return shouldUsePlugin(completedURL, mimeType, shouldPreferPlugInsForImages, false, useFallback); |
| } |
| |
| bool SubframeLoader::requestPlugin(HTMLPlugInImageElement* ownerElement, const KURL& url, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback) |
| { |
| Settings* settings = m_frame->settings(); |
| if ((!allowPlugins(AboutToInstantiatePlugin) |
| // Application plug-ins are plug-ins implemented by the user agent, for example Qt plug-ins, |
| // as opposed to third-party code such as Flash. The user agent decides whether or not they are |
| // permitted, rather than WebKit. |
| && !MIMETypeRegistry::isApplicationPluginMIMEType(mimeType)) |
| || (!settings->isJavaEnabled() && MIMETypeRegistry::isJavaAppletMIMEType(mimeType))) |
| return false; |
| |
| if (m_frame->document()) { |
| if (m_frame->document()->securityOrigin()->isSandboxed(SandboxPlugins)) |
| return false; |
| if (!m_frame->document()->contentSecurityPolicy()->allowObjectFromSource(url)) |
| return false; |
| } |
| |
| ASSERT(ownerElement->hasTagName(objectTag) || ownerElement->hasTagName(embedTag)); |
| return loadPlugin(ownerElement, url, mimeType, paramNames, paramValues, useFallback); |
| } |
| |
| bool SubframeLoader::requestObject(HTMLPlugInImageElement* ownerElement, const String& url, const AtomicString& frameName, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues) |
| { |
| if (url.isEmpty() && mimeType.isEmpty()) |
| return false; |
| |
| // FIXME: None of this code should use renderers! |
| RenderEmbeddedObject* renderer = ownerElement->renderEmbeddedObject(); |
| ASSERT(renderer); |
| if (!renderer) |
| return false; |
| |
| KURL completedURL; |
| if (!url.isEmpty()) |
| completedURL = completeURL(url); |
| |
| bool useFallback; |
| if (shouldUsePlugin(completedURL, mimeType, ownerElement->shouldPreferPlugInsForImages(), renderer->hasFallbackContent(), useFallback)) |
| return requestPlugin(ownerElement, completedURL, mimeType, paramNames, paramValues, useFallback); |
| |
| // If the plug-in element already contains a subframe, loadOrRedirectSubframe will re-use it. Otherwise, |
| // it will create a new frame and set it as the RenderPart's widget, causing what was previously |
| // in the widget to be torn down. |
| return loadOrRedirectSubframe(ownerElement, completedURL, frameName, true, true); |
| } |
| |
| #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) |
| PassRefPtr<Widget> SubframeLoader::loadMediaPlayerProxyPlugin(Node* node, const KURL& url, |
| const Vector<String>& paramNames, const Vector<String>& paramValues) |
| { |
| ASSERT(node->hasTagName(videoTag) || node->hasTagName(audioTag)); |
| |
| KURL completedURL; |
| if (!url.isEmpty()) |
| completedURL = completeURL(url); |
| |
| if (!m_frame->document()->securityOrigin()->canDisplay(completedURL)) { |
| FrameLoader::reportLocalLoadFailed(m_frame, completedURL.string()); |
| return 0; |
| } |
| |
| HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(node); |
| RenderPart* renderer = toRenderPart(node->renderer()); |
| IntSize size; |
| |
| if (renderer) |
| size = IntSize(renderer->contentWidth(), renderer->contentHeight()); |
| else if (mediaElement->isVideo()) |
| size = RenderVideo::defaultSize(); |
| |
| m_frame->loader()->checkIfRunInsecureContent(m_frame->document()->securityOrigin(), completedURL); |
| |
| RefPtr<Widget> widget = m_frame->loader()->client()->createMediaPlayerProxyPlugin(size, mediaElement, completedURL, |
| paramNames, paramValues, "application/x-media-element-proxy-plugin"); |
| |
| if (widget && renderer) { |
| renderer->setWidget(widget); |
| renderer->node()->setNeedsStyleRecalc(SyntheticStyleChange); |
| } |
| m_containsPlugins = true; |
| |
| return widget ? widget.release() : 0; |
| } |
| #endif // ENABLE(PLUGIN_PROXY_FOR_VIDEO) |
| |
| PassRefPtr<Widget> SubframeLoader::createJavaAppletWidget(const IntSize& size, HTMLAppletElement* element, const HashMap<String, String>& args) |
| { |
| String baseURLString; |
| String codeBaseURLString; |
| Vector<String> paramNames; |
| Vector<String> paramValues; |
| HashMap<String, String>::const_iterator end = args.end(); |
| for (HashMap<String, String>::const_iterator it = args.begin(); it != end; ++it) { |
| if (equalIgnoringCase(it->first, "baseurl")) |
| baseURLString = it->second; |
| else if (equalIgnoringCase(it->first, "codebase")) |
| codeBaseURLString = it->second; |
| paramNames.append(it->first); |
| paramValues.append(it->second); |
| } |
| |
| if (!codeBaseURLString.isEmpty()) { |
| KURL codeBaseURL = completeURL(codeBaseURLString); |
| if (!element->document()->securityOrigin()->canDisplay(codeBaseURL)) { |
| FrameLoader::reportLocalLoadFailed(m_frame, codeBaseURL.string()); |
| return 0; |
| } |
| } |
| |
| if (baseURLString.isEmpty()) |
| baseURLString = m_frame->document()->baseURL().string(); |
| KURL baseURL = completeURL(baseURLString); |
| |
| RefPtr<Widget> widget; |
| if (allowPlugins(AboutToInstantiatePlugin)) |
| widget = m_frame->loader()->client()->createJavaAppletWidget(size, element, baseURL, paramNames, paramValues); |
| if (!widget) |
| return 0; |
| |
| m_containsPlugins = true; |
| return widget; |
| } |
| |
| Frame* SubframeLoader::loadOrRedirectSubframe(HTMLFrameOwnerElement* ownerElement, const KURL& url, const AtomicString& frameName, bool lockHistory, bool lockBackForwardList) |
| { |
| Frame* frame = ownerElement->contentFrame(); |
| if (frame) |
| frame->navigationScheduler()->scheduleLocationChange(m_frame->document()->securityOrigin(), url.string(), m_frame->loader()->outgoingReferrer(), lockHistory, lockBackForwardList); |
| else |
| frame = loadSubframe(ownerElement, url, frameName, m_frame->loader()->outgoingReferrer()); |
| return frame; |
| } |
| |
| Frame* SubframeLoader::loadSubframe(HTMLFrameOwnerElement* ownerElement, const KURL& url, const String& name, const String& referrer) |
| { |
| bool allowsScrolling = true; |
| int marginWidth = -1; |
| int marginHeight = -1; |
| if (ownerElement->hasTagName(frameTag) || ownerElement->hasTagName(iframeTag)) { |
| HTMLFrameElementBase* o = static_cast<HTMLFrameElementBase*>(ownerElement); |
| allowsScrolling = o->scrollingMode() != ScrollbarAlwaysOff; |
| marginWidth = o->marginWidth(); |
| marginHeight = o->marginHeight(); |
| } |
| |
| if (!ownerElement->document()->securityOrigin()->canDisplay(url)) { |
| FrameLoader::reportLocalLoadFailed(m_frame, url.string()); |
| return 0; |
| } |
| |
| bool hideReferrer = SecurityOrigin::shouldHideReferrer(url, referrer); |
| RefPtr<Frame> frame = m_frame->loader()->client()->createFrame(url, name, ownerElement, hideReferrer ? String() : referrer, allowsScrolling, marginWidth, marginHeight); |
| |
| if (!frame) { |
| m_frame->loader()->checkCallImplicitClose(); |
| return 0; |
| } |
| |
| // All new frames will have m_isComplete set to true at this point due to synchronously loading |
| // an empty document in FrameLoader::init(). But many frames will now be starting an |
| // asynchronous load of url, so we set m_isComplete to false and then check if the load is |
| // actually completed below. (Note that we set m_isComplete to false even for synchronous |
| // loads, so that checkCompleted() below won't bail early.) |
| // FIXME: Can we remove this entirely? m_isComplete normally gets set to false when a load is committed. |
| frame->loader()->started(); |
| |
| RenderObject* renderer = ownerElement->renderer(); |
| FrameView* view = frame->view(); |
| if (renderer && renderer->isWidget() && view) |
| toRenderWidget(renderer)->setWidget(view); |
| |
| m_frame->loader()->checkCallImplicitClose(); |
| |
| // Some loads are performed synchronously (e.g., about:blank and loads |
| // cancelled by returning a null ResourceRequest from requestFromDelegate). |
| // In these cases, the synchronous load would have finished |
| // before we could connect the signals, so make sure to send the |
| // completed() signal for the child by hand and mark the load as being |
| // complete. |
| // FIXME: In this case the Frame will have finished loading before |
| // it's being added to the child list. It would be a good idea to |
| // create the child first, then invoke the loader separately. |
| if (frame->loader()->state() == FrameStateComplete && !frame->loader()->policyDocumentLoader()) |
| frame->loader()->checkCompleted(); |
| |
| return frame.get(); |
| } |
| |
| bool SubframeLoader::allowPlugins(ReasonForCallingAllowPlugins reason) |
| { |
| Settings* settings = m_frame->settings(); |
| bool allowed = m_frame->loader()->client()->allowPlugins(settings && settings->arePluginsEnabled()); |
| if (!allowed && reason == AboutToInstantiatePlugin) |
| m_frame->loader()->client()->didNotAllowPlugins(); |
| return allowed; |
| } |
| |
| bool SubframeLoader::shouldUsePlugin(const KURL& url, const String& mimeType, bool shouldPreferPlugInsForImages, bool hasFallback, bool& useFallback) |
| { |
| if (m_frame->loader()->client()->shouldUsePluginDocument(mimeType)) { |
| useFallback = false; |
| return true; |
| } |
| |
| // Allow other plug-ins to win over QuickTime because if the user has installed a plug-in that |
| // can handle TIFF (which QuickTime can also handle) they probably intended to override QT. |
| if (m_frame->page() && (mimeType == "image/tiff" || mimeType == "image/tif" || mimeType == "image/x-tiff")) { |
| const PluginData* pluginData = m_frame->page()->pluginData(); |
| String pluginName = pluginData ? pluginData->pluginNameForMimeType(mimeType) : String(); |
| if (!pluginName.isEmpty() && !pluginName.contains("QuickTime", false)) |
| return true; |
| } |
| |
| ObjectContentType objectType = m_frame->loader()->client()->objectContentType(url, mimeType, shouldPreferPlugInsForImages); |
| // If an object's content can't be handled and it has no fallback, let |
| // it be handled as a plugin to show the broken plugin icon. |
| useFallback = objectType == ObjectContentNone && hasFallback; |
| return objectType == ObjectContentNone || objectType == ObjectContentNetscapePlugin || objectType == ObjectContentOtherPlugin; |
| } |
| |
| Document* SubframeLoader::document() const |
| { |
| return m_frame->document(); |
| } |
| |
| bool SubframeLoader::loadPlugin(HTMLPlugInImageElement* pluginElement, const KURL& url, const String& mimeType, |
| const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback) |
| { |
| RenderEmbeddedObject* renderer = pluginElement->renderEmbeddedObject(); |
| |
| // FIXME: This code should not depend on renderer! |
| if (!renderer || useFallback) |
| return false; |
| |
| if (!document()->securityOrigin()->canDisplay(url)) { |
| FrameLoader::reportLocalLoadFailed(m_frame, url.string()); |
| return false; |
| } |
| |
| FrameLoader* frameLoader = m_frame->loader(); |
| frameLoader->checkIfRunInsecureContent(document()->securityOrigin(), url); |
| |
| IntSize contentSize(renderer->contentWidth(), renderer->contentHeight()); |
| bool loadManually = document()->isPluginDocument() && !m_containsPlugins && toPluginDocument(document())->shouldLoadPluginManually(); |
| RefPtr<Widget> widget = frameLoader->client()->createPlugin(contentSize, |
| pluginElement, url, paramNames, paramValues, mimeType, loadManually); |
| |
| if (!widget) { |
| renderer->setShowsMissingPluginIndicator(); |
| return false; |
| } |
| |
| renderer->setWidget(widget); |
| m_containsPlugins = true; |
| |
| #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) || ENABLE(3D_PLUGIN) |
| pluginElement->setNeedsStyleRecalc(SyntheticStyleChange); |
| #endif |
| return true; |
| } |
| |
| KURL SubframeLoader::completeURL(const String& url) const |
| { |
| ASSERT(m_frame->document()); |
| return m_frame->document()->completeURL(url); |
| } |
| |
| } // namespace WebCore |