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"
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
)
31 MaybeCloseWindowHelper::MaybeCloseWindowHelper(nsIInterfaceRequestor
* aContentContext
)
32 : mContentContext(aContentContext
)
33 , mWindowToClose(nullptr)
35 , mShouldCloseWindow(false)
39 MaybeCloseWindowHelper::~MaybeCloseWindowHelper()
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
;
74 MaybeCloseWindowHelper::Notify(nsITimer
* timer
)
76 NS_ASSERTION(mWindowToClose
, "No window to close after timer fired");
78 mWindowToClose
->Close();
79 mWindowToClose
= nullptr;
85 nsDSURIContentListener::nsDSURIContentListener(nsDocShell
* aDocShell
)
86 : mDocShell(aDocShell
)
87 , mExistingJPEGRequest(nullptr)
88 , mParentContentListener(nullptr)
92 nsDSURIContentListener::~nsDSURIContentListener()
97 nsDSURIContentListener::Init()
100 mNavInfo
= do_GetService(NS_WEBNAVIGATION_INFO_CONTRACTID
, &rv
);
101 NS_ASSERTION(NS_SUCCEEDED(rv
), "Failed to get webnav info");
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
)
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.
124 nsCOMPtr
<nsIURIContentListener
> parentListener
;
125 GetParentContentListener(getter_AddRefs(parentListener
));
126 if (parentListener
) {
127 return parentListener
->OnStartURIOpen(aURI
, aAbortOpen
);
134 nsDSURIContentListener::DoContent(const nsACString
& aContentType
,
135 bool aIsContentPreferred
,
136 nsIRequest
* aRequest
,
137 nsIStreamListener
** aContentHandler
,
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
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();
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
);
196 rv
= mDocShell
->CreateContentViewer(aContentType
, aRequest
, aContentHandler
);
197 if (NS_SUCCEEDED(rv
) && reuseCV
) {
198 mExistingJPEGStreamListener
= *aContentHandler
;
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;
212 // we don't know how to handle the content
213 *aContentHandler
= nullptr;
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
);
228 nsDSURIContentListener::IsPreferred(const char* aContentType
,
229 char** aDesiredContentType
,
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
,
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
255 return CanHandleContent(aContentType
, true, aDesiredContentType
, aCanHandle
);
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;
272 uint32_t canHandle
= nsIWebNavigationInfo::UNSUPPORTED
;
273 rv
= mNavInfo
->IsTypeSupported(nsDependentCString(aContentType
),
276 *aCanHandleContent
= (canHandle
!= nsIWebNavigationInfo::UNSUPPORTED
);
283 nsDSURIContentListener::GetLoadCookie(nsISupports
** aLoadCookie
)
285 NS_IF_ADDREF(*aLoadCookie
= nsDocShell::GetAsSupports(mDocShell
));
290 nsDSURIContentListener::SetLoadCookie(nsISupports
* aLoadCookie
)
293 RefPtr
<nsDocLoader
> cookieAsDocLoader
=
294 nsDocLoader::GetAsDocLoader(aLoadCookie
);
295 NS_ASSERTION(cookieAsDocLoader
&& cookieAsDocLoader
== mDocShell
,
296 "Invalid load cookie being set!");
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
);
311 *aParentListener
= mParentContentListener
;
312 NS_IF_ADDREF(*aParentListener
);
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
;
330 mWeakParentContentListener
= nullptr;
331 mParentContentListener
= nullptr;