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