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 "mozilla/dom/CanonicalBrowsingContext.h"
14 #include "mozilla/dom/Document.h"
15 #include "mozilla/dom/WindowGlobalParent.h"
16 #include "mozilla/Unused.h"
18 #include "nsContentSecurityManager.h"
19 #include "nsDocShellLoadTypes.h"
20 #include "nsIInterfaceRequestor.h"
21 #include "nsIMultiPartChannel.h"
22 #include "nsWebNavigationInfo.h"
24 using namespace mozilla
;
25 using namespace mozilla::dom
;
27 NS_IMPL_ADDREF(MaybeCloseWindowHelper
)
28 NS_IMPL_RELEASE(MaybeCloseWindowHelper
)
30 NS_INTERFACE_MAP_BEGIN(MaybeCloseWindowHelper
)
31 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsITimerCallback
)
32 NS_INTERFACE_MAP_ENTRY(nsITimerCallback
)
33 NS_INTERFACE_MAP_ENTRY(nsINamed
)
36 MaybeCloseWindowHelper::MaybeCloseWindowHelper(BrowsingContext
* aContentContext
)
37 : mBrowsingContext(aContentContext
),
39 mShouldCloseWindow(false) {}
41 MaybeCloseWindowHelper::~MaybeCloseWindowHelper() {}
43 void MaybeCloseWindowHelper::SetShouldCloseWindow(bool aShouldCloseWindow
) {
44 mShouldCloseWindow
= aShouldCloseWindow
;
47 BrowsingContext
* MaybeCloseWindowHelper::MaybeCloseWindow() {
48 if (!mShouldCloseWindow
) {
49 return mBrowsingContext
;
52 // This method should not be called more than once, but it's better to avoid
53 // closing the current window again.
54 mShouldCloseWindow
= false;
56 // Reset the window context to the opener window so that the dependent
57 // dialogs have a parent
58 RefPtr
<BrowsingContext
> newBC
= ChooseNewBrowsingContext(mBrowsingContext
);
60 if (newBC
!= mBrowsingContext
&& newBC
&& !newBC
->IsDiscarded()) {
61 mBCToClose
= mBrowsingContext
;
62 mBrowsingContext
= newBC
;
64 // Now close the old window. Do it on a timer so that we don't run
65 // into issues trying to close the window before it has fully opened.
66 NS_ASSERTION(!mTimer
, "mTimer was already initialized once!");
67 NS_NewTimerWithCallback(getter_AddRefs(mTimer
), this, 0,
68 nsITimer::TYPE_ONE_SHOT
);
71 return mBrowsingContext
;
74 already_AddRefed
<BrowsingContext
>
75 MaybeCloseWindowHelper::ChooseNewBrowsingContext(BrowsingContext
* aBC
) {
76 RefPtr
<BrowsingContext
> opener
= aBC
->GetOpener();
77 if (opener
&& !opener
->IsDiscarded()) {
78 return opener
.forget();
81 if (!XRE_IsParentProcess()) {
85 opener
= BrowsingContext::Get(aBC
->Canonical()->GetCrossGroupOpenerId());
86 if (!opener
|| opener
->IsDiscarded()) {
89 return opener
.forget();
93 MaybeCloseWindowHelper::Notify(nsITimer
* timer
) {
94 NS_ASSERTION(mBCToClose
, "No window to close after timer fired");
96 mBCToClose
->Close(CallerType::System
, IgnoreErrors());
104 MaybeCloseWindowHelper::GetName(nsACString
& aName
) {
105 aName
.AssignLiteral("MaybeCloseWindowHelper");
109 nsDSURIContentListener::nsDSURIContentListener(nsDocShell
* aDocShell
)
110 : mDocShell(aDocShell
),
111 mExistingJPEGRequest(nullptr),
112 mParentContentListener(nullptr) {}
114 nsDSURIContentListener::~nsDSURIContentListener() {}
116 NS_IMPL_ADDREF(nsDSURIContentListener
)
117 NS_IMPL_RELEASE(nsDSURIContentListener
)
119 NS_INTERFACE_MAP_BEGIN(nsDSURIContentListener
)
120 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIURIContentListener
)
121 NS_INTERFACE_MAP_ENTRY(nsIURIContentListener
)
122 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
126 nsDSURIContentListener::DoContent(const nsACString
& aContentType
,
127 bool aIsContentPreferred
,
128 nsIRequest
* aRequest
,
129 nsIStreamListener
** aContentHandler
,
130 bool* aAbortProcess
) {
132 NS_ENSURE_ARG_POINTER(aContentHandler
);
133 NS_ENSURE_TRUE(mDocShell
, NS_ERROR_FAILURE
);
134 RefPtr
<nsDocShell
> docShell
= mDocShell
;
136 *aAbortProcess
= false;
138 // determine if the channel has just been retargeted to us...
139 nsLoadFlags loadFlags
= 0;
140 if (nsCOMPtr
<nsIChannel
> openedChannel
= do_QueryInterface(aRequest
)) {
141 openedChannel
->GetLoadFlags(&loadFlags
);
144 if (loadFlags
& nsIChannel::LOAD_RETARGETED_DOCUMENT_URI
) {
145 // XXX: Why does this not stop the content too?
146 docShell
->Stop(nsIWebNavigation::STOP_NETWORK
);
147 NS_ENSURE_TRUE(mDocShell
, NS_ERROR_FAILURE
);
148 docShell
->SetLoadType(aIsContentPreferred
? LOAD_LINK
: LOAD_NORMAL
);
151 // In case of multipart jpeg request (mjpeg) we don't really want to
152 // create new viewer since the one we already have is capable of
153 // rendering multipart jpeg correctly (see bug 625012)
154 nsCOMPtr
<nsIChannel
> baseChannel
;
155 if (nsCOMPtr
<nsIMultiPartChannel
> mpchan
= do_QueryInterface(aRequest
)) {
156 mpchan
->GetBaseChannel(getter_AddRefs(baseChannel
));
159 bool reuseCV
= baseChannel
&& baseChannel
== mExistingJPEGRequest
&&
160 aContentType
.EqualsLiteral("image/jpeg");
162 if (mExistingJPEGStreamListener
&& reuseCV
) {
163 RefPtr
<nsIStreamListener
> copy(mExistingJPEGStreamListener
);
164 copy
.forget(aContentHandler
);
167 rv
= docShell
->CreateContentViewer(aContentType
, aRequest
, aContentHandler
);
168 if (NS_SUCCEEDED(rv
) && reuseCV
) {
169 mExistingJPEGStreamListener
= *aContentHandler
;
171 mExistingJPEGStreamListener
= nullptr;
173 mExistingJPEGRequest
= baseChannel
;
176 if (rv
== NS_ERROR_DOCSHELL_DYING
) {
177 aRequest
->Cancel(rv
);
178 *aAbortProcess
= true;
183 // we don't know how to handle the content
184 nsCOMPtr
<nsIStreamListener
> forget
= dont_AddRef(*aContentHandler
);
185 *aContentHandler
= nullptr;
189 if (loadFlags
& nsIChannel::LOAD_RETARGETED_DOCUMENT_URI
) {
190 nsCOMPtr
<nsPIDOMWindowOuter
> domWindow
=
191 mDocShell
? mDocShell
->GetWindow() : nullptr;
192 NS_ENSURE_TRUE(domWindow
, NS_ERROR_FAILURE
);
193 domWindow
->Focus(mozilla::dom::CallerType::System
);
200 nsDSURIContentListener::IsPreferred(const char* aContentType
,
201 char** aDesiredContentType
,
203 NS_ENSURE_ARG_POINTER(aCanHandle
);
204 NS_ENSURE_ARG_POINTER(aDesiredContentType
);
206 // the docshell has no idea if it is the preferred content provider or not.
207 // It needs to ask its parent if it is the preferred content handler or not...
209 nsCOMPtr
<nsIURIContentListener
> parentListener
;
210 GetParentContentListener(getter_AddRefs(parentListener
));
211 if (parentListener
) {
212 return parentListener
->IsPreferred(aContentType
, aDesiredContentType
,
215 // we used to return false here if we didn't have a parent properly registered
216 // at the top of the docshell hierarchy to dictate what content types this
217 // docshell should be a preferred handler for. But this really makes it hard
218 // for developers using iframe or browser tags because then they need to make
219 // sure they implement nsIURIContentListener otherwise all link clicks would
220 // get sent to another window because we said we weren't the preferred handler
221 // type. I'm going to change the default now... if we can handle the content,
222 // and someone didn't EXPLICITLY set a nsIURIContentListener at the top of our
223 // docshell chain, then we'll now always attempt to process the content
225 return CanHandleContent(aContentType
, true, aDesiredContentType
, aCanHandle
);
229 nsDSURIContentListener::CanHandleContent(const char* aContentType
,
230 bool aIsContentPreferred
,
231 char** aDesiredContentType
,
232 bool* aCanHandleContent
) {
233 MOZ_ASSERT(aCanHandleContent
, "Null out param?");
234 NS_ENSURE_ARG_POINTER(aDesiredContentType
);
236 *aCanHandleContent
= false;
237 *aDesiredContentType
= nullptr;
241 nsWebNavigationInfo::IsTypeSupported(nsDependentCString(aContentType
));
242 *aCanHandleContent
= (canHandle
!= nsIWebNavigationInfo::UNSUPPORTED
);
249 nsDSURIContentListener::GetLoadCookie(nsISupports
** aLoadCookie
) {
250 NS_IF_ADDREF(*aLoadCookie
= nsDocShell::GetAsSupports(mDocShell
));
255 nsDSURIContentListener::SetLoadCookie(nsISupports
* aLoadCookie
) {
257 RefPtr
<nsDocLoader
> cookieAsDocLoader
=
258 nsDocLoader::GetAsDocLoader(aLoadCookie
);
259 NS_ASSERTION(cookieAsDocLoader
&& cookieAsDocLoader
== mDocShell
,
260 "Invalid load cookie being set!");
266 nsDSURIContentListener::GetParentContentListener(
267 nsIURIContentListener
** aParentListener
) {
268 if (mWeakParentContentListener
) {
269 nsCOMPtr
<nsIURIContentListener
> tempListener
=
270 do_QueryReferent(mWeakParentContentListener
);
271 *aParentListener
= tempListener
;
272 NS_IF_ADDREF(*aParentListener
);
274 *aParentListener
= mParentContentListener
;
275 NS_IF_ADDREF(*aParentListener
);
281 nsDSURIContentListener::SetParentContentListener(
282 nsIURIContentListener
* aParentListener
) {
283 if (aParentListener
) {
284 // Store the parent listener as a weak ref. Parents not supporting
285 // nsISupportsWeakReference assert but may still be used.
286 mParentContentListener
= nullptr;
287 mWeakParentContentListener
= do_GetWeakReference(aParentListener
);
288 if (!mWeakParentContentListener
) {
289 mParentContentListener
= aParentListener
;
292 mWeakParentContentListener
= nullptr;
293 mParentContentListener
= nullptr;