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