|
1 /* |
|
2 * Copyright (C) 2006, 2007, 2008, 2009, 2010 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 "Frame.h" |
|
37 #include "FrameLoaderClient.h" |
|
38 #include "HTMLAppletElement.h" |
|
39 #include "HTMLFrameElementBase.h" |
|
40 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) |
|
41 #include "HTMLMediaElement.h" |
|
42 #endif |
|
43 #include "HTMLNames.h" |
|
44 #include "HTMLPlugInElement.h" |
|
45 #include "MIMETypeRegistry.h" |
|
46 #include "Node.h" |
|
47 #include "Page.h" |
|
48 #include "PluginData.h" |
|
49 #include "RenderEmbeddedObject.h" |
|
50 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) |
|
51 #include "RenderVideo.h" |
|
52 #endif |
|
53 #include "RenderView.h" |
|
54 #include "Settings.h" |
|
55 #include "XSSAuditor.h" |
|
56 |
|
57 namespace WebCore { |
|
58 |
|
59 using namespace HTMLNames; |
|
60 |
|
61 SubframeLoader::SubframeLoader(Frame* frame) |
|
62 : m_containsPlugins(false) |
|
63 , m_frame(frame) |
|
64 { |
|
65 } |
|
66 |
|
67 static HTMLPlugInElement* toPlugInElement(Node* node) |
|
68 { |
|
69 if (!node) |
|
70 return 0; |
|
71 |
|
72 ASSERT(node->hasTagName(objectTag) || node->hasTagName(embedTag) || node->hasTagName(appletTag)); |
|
73 |
|
74 return static_cast<HTMLPlugInElement*>(node); |
|
75 } |
|
76 |
|
77 void SubframeLoader::clear() |
|
78 { |
|
79 m_containsPlugins = false; |
|
80 } |
|
81 |
|
82 bool SubframeLoader::requestFrame(HTMLFrameOwnerElement* ownerElement, const String& urlString, const AtomicString& frameName, bool lockHistory, bool lockBackForwardList) |
|
83 { |
|
84 // Support for <frame src="javascript:string"> |
|
85 KURL scriptURL; |
|
86 KURL url; |
|
87 if (protocolIsJavaScript(urlString)) { |
|
88 scriptURL = completeURL(urlString); // completeURL() encodes the URL. |
|
89 url = blankURL(); |
|
90 } else |
|
91 url = completeURL(urlString); |
|
92 |
|
93 Frame* frame = ownerElement->contentFrame(); |
|
94 if (frame) |
|
95 frame->redirectScheduler()->scheduleLocationChange(url.string(), m_frame->loader()->outgoingReferrer(), lockHistory, lockBackForwardList, m_frame->loader()->isProcessingUserGesture()); |
|
96 else |
|
97 frame = loadSubframe(ownerElement, url, frameName, m_frame->loader()->outgoingReferrer()); |
|
98 |
|
99 if (!frame) |
|
100 return false; |
|
101 |
|
102 if (!scriptURL.isEmpty()) |
|
103 frame->script()->executeIfJavaScriptURL(scriptURL); |
|
104 |
|
105 return true; |
|
106 } |
|
107 |
|
108 bool SubframeLoader::requestObject(RenderEmbeddedObject* renderer, const String& url, const AtomicString& frameName, |
|
109 const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues) |
|
110 { |
|
111 if (url.isEmpty() && mimeType.isEmpty()) |
|
112 return false; |
|
113 |
|
114 if (!m_frame->script()->xssAuditor()->canLoadObject(url)) { |
|
115 // It is unsafe to honor the request for this object. |
|
116 return false; |
|
117 } |
|
118 |
|
119 KURL completedURL; |
|
120 if (!url.isEmpty()) |
|
121 completedURL = completeURL(url); |
|
122 |
|
123 bool useFallback; |
|
124 if (shouldUsePlugin(completedURL, mimeType, renderer->hasFallbackContent(), useFallback)) { |
|
125 Settings* settings = m_frame->settings(); |
|
126 if ((!allowPlugins(AboutToInstantiatePlugin) |
|
127 // Application plugins are plugins implemented by the user agent, for example Qt plugins, |
|
128 // as opposed to third-party code such as flash. The user agent decides whether or not they are |
|
129 // permitted, rather than WebKit. |
|
130 && !MIMETypeRegistry::isApplicationPluginMIMEType(mimeType)) |
|
131 || (!settings->isJavaEnabled() && MIMETypeRegistry::isJavaAppletMIMEType(mimeType))) |
|
132 return false; |
|
133 if (m_frame->document() && m_frame->document()->securityOrigin()->isSandboxed(SandboxPlugins)) |
|
134 return false; |
|
135 return loadPlugin(renderer, completedURL, mimeType, paramNames, paramValues, useFallback); |
|
136 } |
|
137 |
|
138 ASSERT(renderer->node()->hasTagName(objectTag) || renderer->node()->hasTagName(embedTag)); |
|
139 HTMLPlugInElement* element = static_cast<HTMLPlugInElement*>(renderer->node()); |
|
140 |
|
141 // If the plug-in element already contains a subframe, requestFrame will re-use it. Otherwise, |
|
142 // it will create a new frame and set it as the RenderPart's widget, causing what was previously |
|
143 // in the widget to be torn down. |
|
144 return requestFrame(element, completedURL, frameName); |
|
145 } |
|
146 |
|
147 |
|
148 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) |
|
149 PassRefPtr<Widget> FrameLoader::loadMediaPlayerProxyPlugin(Node* node, const KURL& url, |
|
150 const Vector<String>& paramNames, const Vector<String>& paramValues) |
|
151 { |
|
152 ASSERT(node->hasTagName(videoTag) || node->hasTagName(audioTag)); |
|
153 |
|
154 if (!m_frame->script()->xssAuditor()->canLoadObject(url.string())) |
|
155 return 0; |
|
156 |
|
157 KURL completedURL; |
|
158 if (!url.isEmpty()) |
|
159 completedURL = completeURL(url); |
|
160 |
|
161 if (!SecurityOrigin::canLoad(completedURL, String(), frame()->document())) { |
|
162 FrameLoader::reportLocalLoadFailed(m_frame, completedURL.string()); |
|
163 return 0; |
|
164 } |
|
165 |
|
166 HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(node); |
|
167 RenderPart* renderer = toRenderPart(node->renderer()); |
|
168 IntSize size; |
|
169 |
|
170 if (renderer) |
|
171 size = IntSize(renderer->contentWidth(), renderer->contentHeight()); |
|
172 else if (mediaElement->isVideo()) |
|
173 size = RenderVideo::defaultSize(); |
|
174 |
|
175 m_frame->loader()->checkIfRunInsecureContent(m_frame->document()->securityOrigin(), completedURL); |
|
176 |
|
177 RefPtr<Widget> widget = m_frame->loader()->client()->createMediaPlayerProxyPlugin(size, mediaElement, completedURL, |
|
178 paramNames, paramValues, "application/x-media-element-proxy-plugin"); |
|
179 |
|
180 if (widget && renderer) { |
|
181 renderer->setWidget(widget); |
|
182 renderer->node()->setNeedsStyleRecalc(SyntheticStyleChange); |
|
183 } |
|
184 m_containsPlugIns = true; |
|
185 |
|
186 return widget ? widget.release() : 0; |
|
187 } |
|
188 |
|
189 void FrameLoader::hideMediaPlayerProxyPlugin(Widget* widget) |
|
190 { |
|
191 m_client->hideMediaPlayerProxyPlugin(widget); |
|
192 } |
|
193 |
|
194 void FrameLoader::showMediaPlayerProxyPlugin(Widget* widget) |
|
195 { |
|
196 m_client->showMediaPlayerProxyPlugin(widget); |
|
197 } |
|
198 |
|
199 #endif // ENABLE(PLUGIN_PROXY_FOR_VIDEO) |
|
200 |
|
201 PassRefPtr<Widget> SubframeLoader::createJavaAppletWidget(const IntSize& size, HTMLAppletElement* element, const HashMap<String, String>& args) |
|
202 { |
|
203 String baseURLString; |
|
204 String codeBaseURLString; |
|
205 Vector<String> paramNames; |
|
206 Vector<String> paramValues; |
|
207 HashMap<String, String>::const_iterator end = args.end(); |
|
208 for (HashMap<String, String>::const_iterator it = args.begin(); it != end; ++it) { |
|
209 if (equalIgnoringCase(it->first, "baseurl")) |
|
210 baseURLString = it->second; |
|
211 else if (equalIgnoringCase(it->first, "codebase")) |
|
212 codeBaseURLString = it->second; |
|
213 paramNames.append(it->first); |
|
214 paramValues.append(it->second); |
|
215 } |
|
216 |
|
217 if (!codeBaseURLString.isEmpty()) { |
|
218 KURL codeBaseURL = completeURL(codeBaseURLString); |
|
219 if (!SecurityOrigin::canLoad(codeBaseURL, String(), element->document())) { |
|
220 FrameLoader::reportLocalLoadFailed(m_frame, codeBaseURL.string()); |
|
221 return 0; |
|
222 } |
|
223 } |
|
224 |
|
225 if (baseURLString.isEmpty()) |
|
226 baseURLString = m_frame->document()->baseURL().string(); |
|
227 KURL baseURL = completeURL(baseURLString); |
|
228 |
|
229 RefPtr<Widget> widget = m_frame->loader()->client()->createJavaAppletWidget(size, element, baseURL, paramNames, paramValues); |
|
230 if (!widget) |
|
231 return 0; |
|
232 |
|
233 m_containsPlugins = true; |
|
234 return widget; |
|
235 } |
|
236 |
|
237 Frame* SubframeLoader::loadSubframe(HTMLFrameOwnerElement* ownerElement, const KURL& url, const String& name, const String& referrer) |
|
238 { |
|
239 bool allowsScrolling = true; |
|
240 int marginWidth = -1; |
|
241 int marginHeight = -1; |
|
242 if (ownerElement->hasTagName(frameTag) || ownerElement->hasTagName(iframeTag)) { |
|
243 HTMLFrameElementBase* o = static_cast<HTMLFrameElementBase*>(ownerElement); |
|
244 allowsScrolling = o->scrollingMode() != ScrollbarAlwaysOff; |
|
245 marginWidth = o->getMarginWidth(); |
|
246 marginHeight = o->getMarginHeight(); |
|
247 } |
|
248 |
|
249 if (!SecurityOrigin::canLoad(url, referrer, 0)) { |
|
250 FrameLoader::reportLocalLoadFailed(m_frame, url.string()); |
|
251 return 0; |
|
252 } |
|
253 |
|
254 bool hideReferrer = SecurityOrigin::shouldHideReferrer(url, referrer); |
|
255 RefPtr<Frame> frame = m_frame->loader()->client()->createFrame(url, name, ownerElement, hideReferrer ? String() : referrer, allowsScrolling, marginWidth, marginHeight); |
|
256 |
|
257 if (!frame) { |
|
258 m_frame->loader()->checkCallImplicitClose(); |
|
259 return 0; |
|
260 } |
|
261 |
|
262 // All new frames will have m_isComplete set to true at this point due to synchronously loading |
|
263 // an empty document in FrameLoader::init(). But many frames will now be starting an |
|
264 // asynchronous load of url, so we set m_isComplete to false and then check if the load is |
|
265 // actually completed below. (Note that we set m_isComplete to false even for synchronous |
|
266 // loads, so that checkCompleted() below won't bail early.) |
|
267 // FIXME: Can we remove this entirely? m_isComplete normally gets set to false when a load is committed. |
|
268 frame->loader()->started(); |
|
269 |
|
270 RenderObject* renderer = ownerElement->renderer(); |
|
271 FrameView* view = frame->view(); |
|
272 if (renderer && renderer->isWidget() && view) |
|
273 toRenderWidget(renderer)->setWidget(view); |
|
274 |
|
275 m_frame->loader()->checkCallImplicitClose(); |
|
276 |
|
277 // Some loads are performed synchronously (e.g., about:blank and loads |
|
278 // cancelled by returning a null ResourceRequest from requestFromDelegate). |
|
279 // In these cases, the synchronous load would have finished |
|
280 // before we could connect the signals, so make sure to send the |
|
281 // completed() signal for the child by hand and mark the load as being |
|
282 // complete. |
|
283 // FIXME: In this case the Frame will have finished loading before |
|
284 // it's being added to the child list. It would be a good idea to |
|
285 // create the child first, then invoke the loader separately. |
|
286 if (frame->loader()->state() == FrameStateComplete && !frame->loader()->policyDocumentLoader()) |
|
287 frame->loader()->checkCompleted(); |
|
288 |
|
289 return frame.get(); |
|
290 } |
|
291 |
|
292 bool SubframeLoader::allowPlugins(ReasonForCallingAllowPlugins reason) |
|
293 { |
|
294 Settings* settings = m_frame->settings(); |
|
295 bool allowed = m_frame->loader()->client()->allowPlugins(settings && settings->arePluginsEnabled()); |
|
296 if (!allowed && reason == AboutToInstantiatePlugin) |
|
297 m_frame->loader()->client()->didNotAllowPlugins(); |
|
298 return allowed; |
|
299 } |
|
300 |
|
301 |
|
302 bool SubframeLoader::shouldUsePlugin(const KURL& url, const String& mimeType, bool hasFallback, bool& useFallback) |
|
303 { |
|
304 if (m_frame->loader()->client()->shouldUsePluginDocument(mimeType)) { |
|
305 useFallback = false; |
|
306 return true; |
|
307 } |
|
308 |
|
309 // Allow other plug-ins to win over QuickTime because if the user has installed a plug-in that |
|
310 // can handle TIFF (which QuickTime can also handle) they probably intended to override QT. |
|
311 if (m_frame->page() && (mimeType == "image/tiff" || mimeType == "image/tif" || mimeType == "image/x-tiff")) { |
|
312 const PluginData* pluginData = m_frame->page()->pluginData(); |
|
313 String pluginName = pluginData ? pluginData->pluginNameForMimeType(mimeType) : String(); |
|
314 if (!pluginName.isEmpty() && !pluginName.contains("QuickTime", false)) |
|
315 return true; |
|
316 } |
|
317 |
|
318 ObjectContentType objectType = m_frame->loader()->client()->objectContentType(url, mimeType); |
|
319 // If an object's content can't be handled and it has no fallback, let |
|
320 // it be handled as a plugin to show the broken plugin icon. |
|
321 useFallback = objectType == ObjectContentNone && hasFallback; |
|
322 return objectType == ObjectContentNone || objectType == ObjectContentNetscapePlugin || objectType == ObjectContentOtherPlugin; |
|
323 } |
|
324 |
|
325 bool SubframeLoader::loadPlugin(RenderEmbeddedObject* renderer, const KURL& url, const String& mimeType, |
|
326 const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback) |
|
327 { |
|
328 RefPtr<Widget> widget; |
|
329 |
|
330 if (renderer && !useFallback) { |
|
331 HTMLPlugInElement* element = toPlugInElement(renderer->node()); |
|
332 |
|
333 if (!SecurityOrigin::canLoad(url, String(), m_frame->document())) { |
|
334 FrameLoader::reportLocalLoadFailed(m_frame, url.string()); |
|
335 return false; |
|
336 } |
|
337 |
|
338 m_frame->loader()->checkIfRunInsecureContent(m_frame->document()->securityOrigin(), url); |
|
339 |
|
340 widget = m_frame->loader()->client()->createPlugin(IntSize(renderer->contentWidth(), renderer->contentHeight()), |
|
341 element, url, paramNames, paramValues, mimeType, |
|
342 m_frame->document()->isPluginDocument() && !m_containsPlugins); |
|
343 if (widget) { |
|
344 renderer->setWidget(widget); |
|
345 m_containsPlugins = true; |
|
346 |
|
347 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) |
|
348 renderer->node()->setNeedsStyleRecalc(SyntheticStyleChange); |
|
349 #endif |
|
350 } else |
|
351 renderer->setShowsMissingPluginIndicator(); |
|
352 } |
|
353 |
|
354 return widget; |
|
355 } |
|
356 |
|
357 KURL SubframeLoader::completeURL(const String& url) const |
|
358 { |
|
359 ASSERT(m_frame->document()); |
|
360 return m_frame->document()->completeURL(url); |
|
361 } |
|
362 |
|
363 } // namespace WebCore |