nsDSURIContentListener.cpp (10183B)
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/. */ 6 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 "nsError.h" 17 #include "nsContentSecurityManager.h" 18 #include "nsDocShellLoadTypes.h" 19 #include "nsIInterfaceRequestor.h" 20 #include "nsIMultiPartChannel.h" 21 #include "nsWebNavigationInfo.h" 22 23 using namespace mozilla; 24 using namespace mozilla::dom; 25 26 NS_IMPL_ADDREF(MaybeCloseWindowHelper) 27 NS_IMPL_RELEASE(MaybeCloseWindowHelper) 28 29 NS_INTERFACE_MAP_BEGIN(MaybeCloseWindowHelper) 30 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITimerCallback) 31 NS_INTERFACE_MAP_ENTRY(nsITimerCallback) 32 NS_INTERFACE_MAP_ENTRY(nsINamed) 33 NS_INTERFACE_MAP_END 34 35 MaybeCloseWindowHelper::MaybeCloseWindowHelper(BrowsingContext* aContentContext) 36 : mBrowsingContext(aContentContext), 37 mTimer(nullptr), 38 mShouldCloseWindow(false) {} 39 40 MaybeCloseWindowHelper::~MaybeCloseWindowHelper() {} 41 42 void MaybeCloseWindowHelper::SetShouldCloseWindow(bool aShouldCloseWindow) { 43 mShouldCloseWindow = aShouldCloseWindow; 44 } 45 46 BrowsingContext* MaybeCloseWindowHelper::MaybeCloseWindow() { 47 if (!mShouldCloseWindow) { 48 return mBrowsingContext; 49 } 50 51 // This method should not be called more than once, but it's better to avoid 52 // closing the current window again. 53 mShouldCloseWindow = false; 54 55 // Reset the window context to the opener window so that the dependent 56 // dialogs have a parent 57 RefPtr<BrowsingContext> newBC = ChooseNewBrowsingContext(mBrowsingContext); 58 59 if (newBC != mBrowsingContext && newBC && !newBC->IsDiscarded()) { 60 mBCToClose = mBrowsingContext; 61 mBrowsingContext = newBC; 62 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, 67 nsITimer::TYPE_ONE_SHOT); 68 } 69 70 return mBrowsingContext; 71 } 72 73 already_AddRefed<BrowsingContext> 74 MaybeCloseWindowHelper::ChooseNewBrowsingContext(BrowsingContext* aBC) { 75 RefPtr<BrowsingContext> opener = aBC->GetOpener(); 76 if (opener && !opener->IsDiscarded()) { 77 return opener.forget(); 78 } 79 80 if (!XRE_IsParentProcess()) { 81 return nullptr; 82 } 83 84 opener = BrowsingContext::Get(aBC->Canonical()->GetCrossGroupOpenerId()); 85 if (!opener || opener->IsDiscarded()) { 86 return nullptr; 87 } 88 return opener.forget(); 89 } 90 91 NS_IMETHODIMP 92 MaybeCloseWindowHelper::Notify(nsITimer* timer) { 93 NS_ASSERTION(mBCToClose, "No window to close after timer fired"); 94 95 mBCToClose->Close(CallerType::System, IgnoreErrors()); 96 mBCToClose = nullptr; 97 mTimer = nullptr; 98 99 return NS_OK; 100 } 101 102 NS_IMETHODIMP 103 MaybeCloseWindowHelper::GetName(nsACString& aName) { 104 aName.AssignLiteral("MaybeCloseWindowHelper"); 105 return NS_OK; 106 } 107 108 nsDSURIContentListener::nsDSURIContentListener(nsDocShell* aDocShell) 109 : mDocShell(aDocShell), 110 mExistingJPEGRequest(nullptr), 111 mParentContentListener(nullptr) {} 112 113 nsDSURIContentListener::~nsDSURIContentListener() {} 114 115 NS_IMPL_ADDREF(nsDSURIContentListener) 116 NS_IMPL_RELEASE(nsDSURIContentListener) 117 118 NS_INTERFACE_MAP_BEGIN(nsDSURIContentListener) 119 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIURIContentListener) 120 NS_INTERFACE_MAP_ENTRY(nsIURIContentListener) 121 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 122 NS_INTERFACE_MAP_END 123 124 NS_IMETHODIMP 125 nsDSURIContentListener::DoContent(const nsACString& aContentType, 126 bool aIsContentPreferred, 127 nsIRequest* aRequest, 128 nsIStreamListener** aContentHandler, 129 bool* aAbortProcess) { 130 nsresult rv; 131 NS_ENSURE_ARG_POINTER(aContentHandler); 132 NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE); 133 RefPtr<nsDocShell> docShell = mDocShell.get(); 134 135 *aAbortProcess = false; 136 137 // determine if the channel has just been retargeted to us... 138 nsLoadFlags loadFlags = 0; 139 if (nsCOMPtr<nsIChannel> openedChannel = do_QueryInterface(aRequest)) { 140 openedChannel->GetLoadFlags(&loadFlags); 141 } 142 143 if (loadFlags & nsIChannel::LOAD_RETARGETED_DOCUMENT_URI) { 144 // XXX: Why does this not stop the content too? 145 docShell->Stop(nsIWebNavigation::STOP_NETWORK); 146 NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE); 147 docShell->SetLoadType(aIsContentPreferred ? LOAD_LINK : LOAD_NORMAL); 148 } 149 150 // In case of multipart jpeg request (mjpeg) we don't really want to 151 // create new viewer since the one we already have is capable of 152 // rendering multipart jpeg correctly (see bug 625012) 153 nsCOMPtr<nsIChannel> baseChannel; 154 if (nsCOMPtr<nsIMultiPartChannel> mpchan = do_QueryInterface(aRequest)) { 155 mpchan->GetBaseChannel(getter_AddRefs(baseChannel)); 156 } 157 158 bool reuseCV = baseChannel && baseChannel == mExistingJPEGRequest && 159 aContentType.EqualsLiteral("image/jpeg"); 160 161 if (mExistingJPEGStreamListener && reuseCV) { 162 RefPtr<nsIStreamListener> copy(mExistingJPEGStreamListener); 163 copy.forget(aContentHandler); 164 rv = NS_OK; 165 } else { 166 rv = 167 docShell->CreateDocumentViewer(aContentType, aRequest, aContentHandler); 168 if (NS_SUCCEEDED(rv) && reuseCV) { 169 mExistingJPEGStreamListener = *aContentHandler; 170 } else { 171 mExistingJPEGStreamListener = nullptr; 172 } 173 mExistingJPEGRequest = baseChannel; 174 } 175 176 if (rv == NS_ERROR_DOCSHELL_DYING) { 177 aRequest->Cancel(rv); 178 *aAbortProcess = true; 179 return NS_OK; 180 } 181 182 if (NS_FAILED(rv)) { 183 // we don't know how to handle the content 184 nsCOMPtr<nsIStreamListener> forget = dont_AddRef(*aContentHandler); 185 *aContentHandler = nullptr; 186 return rv; 187 } 188 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); 194 } 195 196 return NS_OK; 197 } 198 199 NS_IMETHODIMP 200 nsDSURIContentListener::IsPreferred(const char* aContentType, 201 char** aDesiredContentType, 202 bool* aCanHandle) { 203 NS_ENSURE_ARG_POINTER(aCanHandle); 204 NS_ENSURE_ARG_POINTER(aDesiredContentType); 205 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... 208 209 nsCOMPtr<nsIURIContentListener> parentListener; 210 GetParentContentListener(getter_AddRefs(parentListener)); 211 if (parentListener) { 212 return parentListener->IsPreferred(aContentType, aDesiredContentType, 213 aCanHandle); 214 } 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 224 // ourselves... 225 return CanHandleContent(aContentType, true, aDesiredContentType, aCanHandle); 226 } 227 228 NS_IMETHODIMP 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); 235 236 *aCanHandleContent = false; 237 *aDesiredContentType = nullptr; 238 239 if (aContentType) { 240 uint32_t canHandle = 241 nsWebNavigationInfo::IsTypeSupported(nsDependentCString(aContentType)); 242 *aCanHandleContent = (canHandle != nsIWebNavigationInfo::UNSUPPORTED); 243 } 244 245 return NS_OK; 246 } 247 248 NS_IMETHODIMP 249 nsDSURIContentListener::GetLoadCookie(nsISupports** aLoadCookie) { 250 NS_IF_ADDREF(*aLoadCookie = nsDocShell::GetAsSupports(mDocShell)); 251 return NS_OK; 252 } 253 254 NS_IMETHODIMP 255 nsDSURIContentListener::SetLoadCookie(nsISupports* aLoadCookie) { 256 #ifdef DEBUG 257 RefPtr<nsDocLoader> cookieAsDocLoader = 258 nsDocLoader::GetAsDocLoader(aLoadCookie); 259 NS_ASSERTION(cookieAsDocLoader && cookieAsDocLoader == mDocShell, 260 "Invalid load cookie being set!"); 261 #endif 262 return NS_OK; 263 } 264 265 NS_IMETHODIMP 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); 273 } else { 274 *aParentListener = mParentContentListener; 275 NS_IF_ADDREF(*aParentListener); 276 } 277 return NS_OK; 278 } 279 280 NS_IMETHODIMP 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; 290 } 291 } else { 292 mWeakParentContentListener = nullptr; 293 mParentContentListener = nullptr; 294 } 295 return NS_OK; 296 }