Bug 1470678 [wpt PR 11640] - Add a test for text-decoration in tables quirk, a=testonly
[gecko.git] / docshell / base / nsDSURIContentListener.cpp
blob9a4df15431c8285558b4c03f69cef54738f38bb8
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsDocShell.h"
8 #include "nsDSURIContentListener.h"
9 #include "nsIChannel.h"
10 #include "nsServiceManagerUtils.h"
11 #include "nsDocShellCID.h"
12 #include "nsIWebNavigationInfo.h"
13 #include "nsIDocument.h"
14 #include "nsIDOMWindow.h"
15 #include "nsIHttpChannel.h"
16 #include "nsError.h"
17 #include "nsContentSecurityManager.h"
18 #include "nsDocShellLoadTypes.h"
19 #include "nsIInterfaceRequestor.h"
20 #include "nsIMultiPartChannel.h"
22 using namespace mozilla;
24 NS_IMPL_ADDREF(MaybeCloseWindowHelper)
25 NS_IMPL_RELEASE(MaybeCloseWindowHelper)
27 NS_INTERFACE_MAP_BEGIN(MaybeCloseWindowHelper)
28 NS_INTERFACE_MAP_ENTRY(nsISupports)
29 NS_INTERFACE_MAP_END
31 MaybeCloseWindowHelper::MaybeCloseWindowHelper(nsIInterfaceRequestor* aContentContext)
32 : mContentContext(aContentContext)
33 , mWindowToClose(nullptr)
34 , mTimer(nullptr)
35 , mShouldCloseWindow(false)
39 MaybeCloseWindowHelper::~MaybeCloseWindowHelper()
43 void
44 MaybeCloseWindowHelper::SetShouldCloseWindow(bool aShouldCloseWindow)
46 mShouldCloseWindow = aShouldCloseWindow;
49 nsIInterfaceRequestor*
50 MaybeCloseWindowHelper::MaybeCloseWindow()
52 nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(mContentContext);
53 NS_ENSURE_TRUE(window, mContentContext);
55 if (mShouldCloseWindow) {
56 // Reset the window context to the opener window so that the dependent
57 // dialogs have a parent
58 nsCOMPtr<nsPIDOMWindowOuter> opener = window->GetOpener();
60 if (opener && !opener->Closed()) {
61 mContentContext = do_GetInterface(opener);
63 // Now close the old window. Do it on a timer so that we don't run
64 // into issues trying to close the window before it has fully opened.
65 NS_ASSERTION(!mTimer, "mTimer was already initialized once!");
66 NS_NewTimerWithCallback(getter_AddRefs(mTimer), this, 0, nsITimer::TYPE_ONE_SHOT);
67 mWindowToClose = window;
70 return mContentContext;
73 NS_IMETHODIMP
74 MaybeCloseWindowHelper::Notify(nsITimer* timer)
76 NS_ASSERTION(mWindowToClose, "No window to close after timer fired");
78 mWindowToClose->Close();
79 mWindowToClose = nullptr;
80 mTimer = nullptr;
82 return NS_OK;
85 nsDSURIContentListener::nsDSURIContentListener(nsDocShell* aDocShell)
86 : mDocShell(aDocShell)
87 , mExistingJPEGRequest(nullptr)
88 , mParentContentListener(nullptr)
92 nsDSURIContentListener::~nsDSURIContentListener()
96 nsresult
97 nsDSURIContentListener::Init()
99 nsresult rv;
100 mNavInfo = do_GetService(NS_WEBNAVIGATION_INFO_CONTRACTID, &rv);
101 NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to get webnav info");
102 return rv;
105 NS_IMPL_ADDREF(nsDSURIContentListener)
106 NS_IMPL_RELEASE(nsDSURIContentListener)
108 NS_INTERFACE_MAP_BEGIN(nsDSURIContentListener)
109 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIURIContentListener)
110 NS_INTERFACE_MAP_ENTRY(nsIURIContentListener)
111 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
112 NS_INTERFACE_MAP_END
114 NS_IMETHODIMP
115 nsDSURIContentListener::OnStartURIOpen(nsIURI* aURI, bool* aAbortOpen)
117 // If mDocShell is null here, that means someone's starting a load in our
118 // docshell after it's already been destroyed. Don't let that happen.
119 if (!mDocShell) {
120 *aAbortOpen = true;
121 return NS_OK;
124 nsCOMPtr<nsIURIContentListener> parentListener;
125 GetParentContentListener(getter_AddRefs(parentListener));
126 if (parentListener) {
127 return parentListener->OnStartURIOpen(aURI, aAbortOpen);
130 return NS_OK;
133 NS_IMETHODIMP
134 nsDSURIContentListener::DoContent(const nsACString& aContentType,
135 bool aIsContentPreferred,
136 nsIRequest* aRequest,
137 nsIStreamListener** aContentHandler,
138 bool* aAbortProcess)
140 nsresult rv;
141 NS_ENSURE_ARG_POINTER(aContentHandler);
142 NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
144 *aAbortProcess = false;
146 // determine if the channel has just been retargeted to us...
147 nsLoadFlags loadFlags = 0;
148 nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(aRequest);
150 if (aOpenedChannel) {
151 aOpenedChannel->GetLoadFlags(&loadFlags);
153 // block top-level data URI navigations if triggered by the web
154 if (!nsContentSecurityManager::AllowTopLevelNavigationToDataURI(aOpenedChannel)) {
155 // logging to console happens within AllowTopLevelNavigationToDataURI
156 aRequest->Cancel(NS_ERROR_DOM_BAD_URI);
157 *aAbortProcess = true;
158 // close the window since the navigation to a data URI was blocked
159 if (mDocShell) {
160 nsCOMPtr<nsIInterfaceRequestor> contentContext =
161 do_QueryInterface(mDocShell->GetWindow());
162 if (contentContext) {
163 RefPtr<MaybeCloseWindowHelper> maybeCloseWindowHelper =
164 new MaybeCloseWindowHelper(contentContext);
165 maybeCloseWindowHelper->SetShouldCloseWindow(true);
166 maybeCloseWindowHelper->MaybeCloseWindow();
169 return NS_OK;
173 if (loadFlags & nsIChannel::LOAD_RETARGETED_DOCUMENT_URI) {
174 // XXX: Why does this not stop the content too?
175 mDocShell->Stop(nsIWebNavigation::STOP_NETWORK);
177 mDocShell->SetLoadType(aIsContentPreferred ? LOAD_LINK : LOAD_NORMAL);
180 // In case of multipart jpeg request (mjpeg) we don't really want to
181 // create new viewer since the one we already have is capable of
182 // rendering multipart jpeg correctly (see bug 625012)
183 nsCOMPtr<nsIChannel> baseChannel;
184 if (nsCOMPtr<nsIMultiPartChannel> mpchan = do_QueryInterface(aRequest)) {
185 mpchan->GetBaseChannel(getter_AddRefs(baseChannel));
188 bool reuseCV = baseChannel && baseChannel == mExistingJPEGRequest &&
189 aContentType.EqualsLiteral("image/jpeg");
191 if (mExistingJPEGStreamListener && reuseCV) {
192 RefPtr<nsIStreamListener> copy(mExistingJPEGStreamListener);
193 copy.forget(aContentHandler);
194 rv = NS_OK;
195 } else {
196 rv = mDocShell->CreateContentViewer(aContentType, aRequest, aContentHandler);
197 if (NS_SUCCEEDED(rv) && reuseCV) {
198 mExistingJPEGStreamListener = *aContentHandler;
199 } else {
200 mExistingJPEGStreamListener = nullptr;
202 mExistingJPEGRequest = baseChannel;
205 if (rv == NS_ERROR_REMOTE_XUL || rv == NS_ERROR_DOCSHELL_DYING) {
206 aRequest->Cancel(rv);
207 *aAbortProcess = true;
208 return NS_OK;
211 if (NS_FAILED(rv)) {
212 // we don't know how to handle the content
213 *aContentHandler = nullptr;
214 return rv;
217 if (loadFlags & nsIChannel::LOAD_RETARGETED_DOCUMENT_URI) {
218 nsCOMPtr<nsPIDOMWindowOuter> domWindow =
219 mDocShell ? mDocShell->GetWindow() : nullptr;
220 NS_ENSURE_TRUE(domWindow, NS_ERROR_FAILURE);
221 domWindow->Focus();
224 return NS_OK;
227 NS_IMETHODIMP
228 nsDSURIContentListener::IsPreferred(const char* aContentType,
229 char** aDesiredContentType,
230 bool* aCanHandle)
232 NS_ENSURE_ARG_POINTER(aCanHandle);
233 NS_ENSURE_ARG_POINTER(aDesiredContentType);
235 // the docshell has no idea if it is the preferred content provider or not.
236 // It needs to ask its parent if it is the preferred content handler or not...
238 nsCOMPtr<nsIURIContentListener> parentListener;
239 GetParentContentListener(getter_AddRefs(parentListener));
240 if (parentListener) {
241 return parentListener->IsPreferred(aContentType,
242 aDesiredContentType,
243 aCanHandle);
245 // we used to return false here if we didn't have a parent properly registered
246 // at the top of the docshell hierarchy to dictate what content types this
247 // docshell should be a preferred handler for. But this really makes it hard
248 // for developers using iframe or browser tags because then they need to make
249 // sure they implement nsIURIContentListener otherwise all link clicks would
250 // get sent to another window because we said we weren't the preferred handler
251 // type. I'm going to change the default now... if we can handle the content,
252 // and someone didn't EXPLICITLY set a nsIURIContentListener at the top of our
253 // docshell chain, then we'll now always attempt to process the content
254 // ourselves...
255 return CanHandleContent(aContentType, true, aDesiredContentType, aCanHandle);
258 NS_IMETHODIMP
259 nsDSURIContentListener::CanHandleContent(const char* aContentType,
260 bool aIsContentPreferred,
261 char** aDesiredContentType,
262 bool* aCanHandleContent)
264 MOZ_ASSERT(aCanHandleContent, "Null out param?");
265 NS_ENSURE_ARG_POINTER(aDesiredContentType);
267 *aCanHandleContent = false;
268 *aDesiredContentType = nullptr;
270 nsresult rv = NS_OK;
271 if (aContentType) {
272 uint32_t canHandle = nsIWebNavigationInfo::UNSUPPORTED;
273 rv = mNavInfo->IsTypeSupported(nsDependentCString(aContentType),
274 mDocShell,
275 &canHandle);
276 *aCanHandleContent = (canHandle != nsIWebNavigationInfo::UNSUPPORTED);
279 return rv;
282 NS_IMETHODIMP
283 nsDSURIContentListener::GetLoadCookie(nsISupports** aLoadCookie)
285 NS_IF_ADDREF(*aLoadCookie = nsDocShell::GetAsSupports(mDocShell));
286 return NS_OK;
289 NS_IMETHODIMP
290 nsDSURIContentListener::SetLoadCookie(nsISupports* aLoadCookie)
292 #ifdef DEBUG
293 RefPtr<nsDocLoader> cookieAsDocLoader =
294 nsDocLoader::GetAsDocLoader(aLoadCookie);
295 NS_ASSERTION(cookieAsDocLoader && cookieAsDocLoader == mDocShell,
296 "Invalid load cookie being set!");
297 #endif
298 return NS_OK;
301 NS_IMETHODIMP
302 nsDSURIContentListener::GetParentContentListener(
303 nsIURIContentListener** aParentListener)
305 if (mWeakParentContentListener) {
306 nsCOMPtr<nsIURIContentListener> tempListener =
307 do_QueryReferent(mWeakParentContentListener);
308 *aParentListener = tempListener;
309 NS_IF_ADDREF(*aParentListener);
310 } else {
311 *aParentListener = mParentContentListener;
312 NS_IF_ADDREF(*aParentListener);
314 return NS_OK;
317 NS_IMETHODIMP
318 nsDSURIContentListener::SetParentContentListener(
319 nsIURIContentListener* aParentListener)
321 if (aParentListener) {
322 // Store the parent listener as a weak ref. Parents not supporting
323 // nsISupportsWeakReference assert but may still be used.
324 mParentContentListener = nullptr;
325 mWeakParentContentListener = do_GetWeakReference(aParentListener);
326 if (!mWeakParentContentListener) {
327 mParentContentListener = aParentListener;
329 } else {
330 mWeakParentContentListener = nullptr;
331 mParentContentListener = nullptr;
333 return NS_OK;