Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #include "nsPluginStreamListenerPeer.h" |
michael@0 | 7 | #include "nsIStreamConverterService.h" |
michael@0 | 8 | #include "nsIHttpChannel.h" |
michael@0 | 9 | #include "nsIHttpChannelInternal.h" |
michael@0 | 10 | #include "nsIFileChannel.h" |
michael@0 | 11 | #include "nsMimeTypes.h" |
michael@0 | 12 | #include "nsISupportsPrimitives.h" |
michael@0 | 13 | #include "nsNetCID.h" |
michael@0 | 14 | #include "nsPluginLogging.h" |
michael@0 | 15 | #include "nsIURI.h" |
michael@0 | 16 | #include "nsIURL.h" |
michael@0 | 17 | #include "nsPluginHost.h" |
michael@0 | 18 | #include "nsIByteRangeRequest.h" |
michael@0 | 19 | #include "nsIMultiPartChannel.h" |
michael@0 | 20 | #include "nsIInputStreamTee.h" |
michael@0 | 21 | #include "nsPrintfCString.h" |
michael@0 | 22 | #include "nsIScriptGlobalObject.h" |
michael@0 | 23 | #include "nsIDocument.h" |
michael@0 | 24 | #include "nsIWebNavigation.h" |
michael@0 | 25 | #include "nsContentUtils.h" |
michael@0 | 26 | #include "nsNetUtil.h" |
michael@0 | 27 | #include "nsPluginNativeWindow.h" |
michael@0 | 28 | #include "GeckoProfiler.h" |
michael@0 | 29 | #include "nsPluginInstanceOwner.h" |
michael@0 | 30 | #include "nsDataHashtable.h" |
michael@0 | 31 | |
michael@0 | 32 | #define MAGIC_REQUEST_CONTEXT 0x01020304 |
michael@0 | 33 | |
michael@0 | 34 | // nsPluginByteRangeStreamListener |
michael@0 | 35 | |
michael@0 | 36 | class nsPluginByteRangeStreamListener |
michael@0 | 37 | : public nsIStreamListener |
michael@0 | 38 | , public nsIInterfaceRequestor |
michael@0 | 39 | { |
michael@0 | 40 | public: |
michael@0 | 41 | nsPluginByteRangeStreamListener(nsIWeakReference* aWeakPtr); |
michael@0 | 42 | virtual ~nsPluginByteRangeStreamListener(); |
michael@0 | 43 | |
michael@0 | 44 | NS_DECL_ISUPPORTS |
michael@0 | 45 | NS_DECL_NSIREQUESTOBSERVER |
michael@0 | 46 | NS_DECL_NSISTREAMLISTENER |
michael@0 | 47 | NS_DECL_NSIINTERFACEREQUESTOR |
michael@0 | 48 | |
michael@0 | 49 | private: |
michael@0 | 50 | nsCOMPtr<nsIStreamListener> mStreamConverter; |
michael@0 | 51 | nsWeakPtr mWeakPtrPluginStreamListenerPeer; |
michael@0 | 52 | bool mRemoveMagicNumber; |
michael@0 | 53 | }; |
michael@0 | 54 | |
michael@0 | 55 | NS_IMPL_ISUPPORTS(nsPluginByteRangeStreamListener, |
michael@0 | 56 | nsIRequestObserver, |
michael@0 | 57 | nsIStreamListener, |
michael@0 | 58 | nsIInterfaceRequestor) |
michael@0 | 59 | |
michael@0 | 60 | nsPluginByteRangeStreamListener::nsPluginByteRangeStreamListener(nsIWeakReference* aWeakPtr) |
michael@0 | 61 | { |
michael@0 | 62 | mWeakPtrPluginStreamListenerPeer = aWeakPtr; |
michael@0 | 63 | mRemoveMagicNumber = false; |
michael@0 | 64 | } |
michael@0 | 65 | |
michael@0 | 66 | nsPluginByteRangeStreamListener::~nsPluginByteRangeStreamListener() |
michael@0 | 67 | { |
michael@0 | 68 | mStreamConverter = 0; |
michael@0 | 69 | mWeakPtrPluginStreamListenerPeer = 0; |
michael@0 | 70 | } |
michael@0 | 71 | |
michael@0 | 72 | /** |
michael@0 | 73 | * Unwrap any byte-range requests so that we can check whether the base channel |
michael@0 | 74 | * is being tracked properly. |
michael@0 | 75 | */ |
michael@0 | 76 | static nsCOMPtr<nsIRequest> |
michael@0 | 77 | GetBaseRequest(nsIRequest* r) |
michael@0 | 78 | { |
michael@0 | 79 | nsCOMPtr<nsIMultiPartChannel> mp = do_QueryInterface(r); |
michael@0 | 80 | if (!mp) |
michael@0 | 81 | return r; |
michael@0 | 82 | |
michael@0 | 83 | nsCOMPtr<nsIChannel> base; |
michael@0 | 84 | mp->GetBaseChannel(getter_AddRefs(base)); |
michael@0 | 85 | return already_AddRefed<nsIRequest>(base.forget()); |
michael@0 | 86 | } |
michael@0 | 87 | |
michael@0 | 88 | NS_IMETHODIMP |
michael@0 | 89 | nsPluginByteRangeStreamListener::OnStartRequest(nsIRequest *request, nsISupports *ctxt) |
michael@0 | 90 | { |
michael@0 | 91 | nsresult rv; |
michael@0 | 92 | |
michael@0 | 93 | nsCOMPtr<nsIStreamListener> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer); |
michael@0 | 94 | if (!finalStreamListener) |
michael@0 | 95 | return NS_ERROR_FAILURE; |
michael@0 | 96 | |
michael@0 | 97 | nsPluginStreamListenerPeer *pslp = |
michael@0 | 98 | static_cast<nsPluginStreamListenerPeer*>(finalStreamListener.get()); |
michael@0 | 99 | |
michael@0 | 100 | NS_ASSERTION(pslp->mRequests.IndexOfObject(GetBaseRequest(request)) != -1, |
michael@0 | 101 | "Untracked byte-range request?"); |
michael@0 | 102 | |
michael@0 | 103 | nsCOMPtr<nsIStreamConverterService> serv = do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv); |
michael@0 | 104 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 105 | rv = serv->AsyncConvertData(MULTIPART_BYTERANGES, |
michael@0 | 106 | "*/*", |
michael@0 | 107 | finalStreamListener, |
michael@0 | 108 | nullptr, |
michael@0 | 109 | getter_AddRefs(mStreamConverter)); |
michael@0 | 110 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 111 | rv = mStreamConverter->OnStartRequest(request, ctxt); |
michael@0 | 112 | if (NS_SUCCEEDED(rv)) |
michael@0 | 113 | return rv; |
michael@0 | 114 | } |
michael@0 | 115 | } |
michael@0 | 116 | mStreamConverter = 0; |
michael@0 | 117 | |
michael@0 | 118 | nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request)); |
michael@0 | 119 | if (!httpChannel) { |
michael@0 | 120 | return NS_ERROR_FAILURE; |
michael@0 | 121 | } |
michael@0 | 122 | |
michael@0 | 123 | uint32_t responseCode = 0; |
michael@0 | 124 | rv = httpChannel->GetResponseStatus(&responseCode); |
michael@0 | 125 | if (NS_FAILED(rv)) { |
michael@0 | 126 | return NS_ERROR_FAILURE; |
michael@0 | 127 | } |
michael@0 | 128 | |
michael@0 | 129 | if (responseCode != 200) { |
michael@0 | 130 | uint32_t wantsAllNetworkStreams = 0; |
michael@0 | 131 | rv = pslp->GetPluginInstance()->GetValueFromPlugin(NPPVpluginWantsAllNetworkStreams, |
michael@0 | 132 | &wantsAllNetworkStreams); |
michael@0 | 133 | // If the call returned an error code make sure we still use our default value. |
michael@0 | 134 | if (NS_FAILED(rv)) { |
michael@0 | 135 | wantsAllNetworkStreams = 0; |
michael@0 | 136 | } |
michael@0 | 137 | |
michael@0 | 138 | if (!wantsAllNetworkStreams){ |
michael@0 | 139 | return NS_ERROR_FAILURE; |
michael@0 | 140 | } |
michael@0 | 141 | } |
michael@0 | 142 | |
michael@0 | 143 | // if server cannot continue with byte range (206 status) and sending us whole object (200 status) |
michael@0 | 144 | // reset this seekable stream & try serve it to plugin instance as a file |
michael@0 | 145 | mStreamConverter = finalStreamListener; |
michael@0 | 146 | mRemoveMagicNumber = true; |
michael@0 | 147 | |
michael@0 | 148 | rv = pslp->ServeStreamAsFile(request, ctxt); |
michael@0 | 149 | return rv; |
michael@0 | 150 | } |
michael@0 | 151 | |
michael@0 | 152 | NS_IMETHODIMP |
michael@0 | 153 | nsPluginByteRangeStreamListener::OnStopRequest(nsIRequest *request, nsISupports *ctxt, |
michael@0 | 154 | nsresult status) |
michael@0 | 155 | { |
michael@0 | 156 | if (!mStreamConverter) |
michael@0 | 157 | return NS_ERROR_FAILURE; |
michael@0 | 158 | |
michael@0 | 159 | nsCOMPtr<nsIStreamListener> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer); |
michael@0 | 160 | if (!finalStreamListener) |
michael@0 | 161 | return NS_ERROR_FAILURE; |
michael@0 | 162 | |
michael@0 | 163 | nsPluginStreamListenerPeer *pslp = |
michael@0 | 164 | static_cast<nsPluginStreamListenerPeer*>(finalStreamListener.get()); |
michael@0 | 165 | bool found = pslp->mRequests.RemoveObject(request); |
michael@0 | 166 | if (!found) { |
michael@0 | 167 | NS_ERROR("OnStopRequest received for untracked byte-range request!"); |
michael@0 | 168 | } |
michael@0 | 169 | |
michael@0 | 170 | if (mRemoveMagicNumber) { |
michael@0 | 171 | // remove magic number from container |
michael@0 | 172 | nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(ctxt); |
michael@0 | 173 | if (container) { |
michael@0 | 174 | uint32_t magicNumber = 0; |
michael@0 | 175 | container->GetData(&magicNumber); |
michael@0 | 176 | if (magicNumber == MAGIC_REQUEST_CONTEXT) { |
michael@0 | 177 | // to allow properly finish nsPluginStreamListenerPeer->OnStopRequest() |
michael@0 | 178 | // set it to something that is not the magic number. |
michael@0 | 179 | container->SetData(0); |
michael@0 | 180 | } |
michael@0 | 181 | } else { |
michael@0 | 182 | NS_WARNING("Bad state of nsPluginByteRangeStreamListener"); |
michael@0 | 183 | } |
michael@0 | 184 | } |
michael@0 | 185 | |
michael@0 | 186 | return mStreamConverter->OnStopRequest(request, ctxt, status); |
michael@0 | 187 | } |
michael@0 | 188 | |
michael@0 | 189 | // CachedFileHolder |
michael@0 | 190 | |
michael@0 | 191 | CachedFileHolder::CachedFileHolder(nsIFile* cacheFile) |
michael@0 | 192 | : mFile(cacheFile) |
michael@0 | 193 | { |
michael@0 | 194 | NS_ASSERTION(mFile, "Empty CachedFileHolder"); |
michael@0 | 195 | } |
michael@0 | 196 | |
michael@0 | 197 | CachedFileHolder::~CachedFileHolder() |
michael@0 | 198 | { |
michael@0 | 199 | mFile->Remove(false); |
michael@0 | 200 | } |
michael@0 | 201 | |
michael@0 | 202 | void |
michael@0 | 203 | CachedFileHolder::AddRef() |
michael@0 | 204 | { |
michael@0 | 205 | ++mRefCnt; |
michael@0 | 206 | NS_LOG_ADDREF(this, mRefCnt, "CachedFileHolder", sizeof(*this)); |
michael@0 | 207 | } |
michael@0 | 208 | |
michael@0 | 209 | void |
michael@0 | 210 | CachedFileHolder::Release() |
michael@0 | 211 | { |
michael@0 | 212 | --mRefCnt; |
michael@0 | 213 | NS_LOG_RELEASE(this, mRefCnt, "CachedFileHolder"); |
michael@0 | 214 | if (0 == mRefCnt) |
michael@0 | 215 | delete this; |
michael@0 | 216 | } |
michael@0 | 217 | |
michael@0 | 218 | |
michael@0 | 219 | NS_IMETHODIMP |
michael@0 | 220 | nsPluginByteRangeStreamListener::OnDataAvailable(nsIRequest *request, nsISupports *ctxt, |
michael@0 | 221 | nsIInputStream *inStr, |
michael@0 | 222 | uint64_t sourceOffset, |
michael@0 | 223 | uint32_t count) |
michael@0 | 224 | { |
michael@0 | 225 | if (!mStreamConverter) |
michael@0 | 226 | return NS_ERROR_FAILURE; |
michael@0 | 227 | |
michael@0 | 228 | nsCOMPtr<nsIStreamListener> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer); |
michael@0 | 229 | if (!finalStreamListener) |
michael@0 | 230 | return NS_ERROR_FAILURE; |
michael@0 | 231 | |
michael@0 | 232 | return mStreamConverter->OnDataAvailable(request, ctxt, inStr, sourceOffset, count); |
michael@0 | 233 | } |
michael@0 | 234 | |
michael@0 | 235 | NS_IMETHODIMP |
michael@0 | 236 | nsPluginByteRangeStreamListener::GetInterface(const nsIID& aIID, void** result) |
michael@0 | 237 | { |
michael@0 | 238 | // Forward interface requests to our parent |
michael@0 | 239 | nsCOMPtr<nsIInterfaceRequestor> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer); |
michael@0 | 240 | if (!finalStreamListener) |
michael@0 | 241 | return NS_ERROR_FAILURE; |
michael@0 | 242 | |
michael@0 | 243 | return finalStreamListener->GetInterface(aIID, result); |
michael@0 | 244 | } |
michael@0 | 245 | |
michael@0 | 246 | // nsPluginStreamListenerPeer |
michael@0 | 247 | |
michael@0 | 248 | NS_IMPL_ISUPPORTS(nsPluginStreamListenerPeer, |
michael@0 | 249 | nsIStreamListener, |
michael@0 | 250 | nsIRequestObserver, |
michael@0 | 251 | nsIHttpHeaderVisitor, |
michael@0 | 252 | nsISupportsWeakReference, |
michael@0 | 253 | nsIInterfaceRequestor, |
michael@0 | 254 | nsIChannelEventSink) |
michael@0 | 255 | |
michael@0 | 256 | nsPluginStreamListenerPeer::nsPluginStreamListenerPeer() |
michael@0 | 257 | { |
michael@0 | 258 | mStreamType = NP_NORMAL; |
michael@0 | 259 | mStartBinding = false; |
michael@0 | 260 | mAbort = false; |
michael@0 | 261 | mRequestFailed = false; |
michael@0 | 262 | |
michael@0 | 263 | mPendingRequests = 0; |
michael@0 | 264 | mHaveFiredOnStartRequest = false; |
michael@0 | 265 | mDataForwardToRequest = nullptr; |
michael@0 | 266 | |
michael@0 | 267 | mSeekable = false; |
michael@0 | 268 | mModified = 0; |
michael@0 | 269 | mStreamOffset = 0; |
michael@0 | 270 | mStreamComplete = 0; |
michael@0 | 271 | } |
michael@0 | 272 | |
michael@0 | 273 | nsPluginStreamListenerPeer::~nsPluginStreamListenerPeer() |
michael@0 | 274 | { |
michael@0 | 275 | #ifdef PLUGIN_LOGGING |
michael@0 | 276 | PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL, |
michael@0 | 277 | ("nsPluginStreamListenerPeer::dtor this=%p, url=%s\n",this, mURLSpec.get())); |
michael@0 | 278 | #endif |
michael@0 | 279 | |
michael@0 | 280 | if (mPStreamListener) { |
michael@0 | 281 | mPStreamListener->SetStreamListenerPeer(nullptr); |
michael@0 | 282 | } |
michael@0 | 283 | |
michael@0 | 284 | // close FD of mFileCacheOutputStream if it's still open |
michael@0 | 285 | // or we won't be able to remove the cache file |
michael@0 | 286 | if (mFileCacheOutputStream) |
michael@0 | 287 | mFileCacheOutputStream = nullptr; |
michael@0 | 288 | |
michael@0 | 289 | delete mDataForwardToRequest; |
michael@0 | 290 | |
michael@0 | 291 | if (mPluginInstance) |
michael@0 | 292 | mPluginInstance->FileCachedStreamListeners()->RemoveElement(this); |
michael@0 | 293 | } |
michael@0 | 294 | |
michael@0 | 295 | // Called as a result of GetURL and PostURL, or by the host in the case of the |
michael@0 | 296 | // initial plugin stream. |
michael@0 | 297 | nsresult nsPluginStreamListenerPeer::Initialize(nsIURI *aURL, |
michael@0 | 298 | nsNPAPIPluginInstance *aInstance, |
michael@0 | 299 | nsNPAPIPluginStreamListener* aListener) |
michael@0 | 300 | { |
michael@0 | 301 | #ifdef PLUGIN_LOGGING |
michael@0 | 302 | nsAutoCString urlSpec; |
michael@0 | 303 | if (aURL != nullptr) aURL->GetSpec(urlSpec); |
michael@0 | 304 | |
michael@0 | 305 | PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL, |
michael@0 | 306 | ("nsPluginStreamListenerPeer::Initialize instance=%p, url=%s\n", aInstance, urlSpec.get())); |
michael@0 | 307 | |
michael@0 | 308 | PR_LogFlush(); |
michael@0 | 309 | #endif |
michael@0 | 310 | |
michael@0 | 311 | // Not gonna work out |
michael@0 | 312 | if (!aInstance) { |
michael@0 | 313 | return NS_ERROR_FAILURE; |
michael@0 | 314 | } |
michael@0 | 315 | |
michael@0 | 316 | mURL = aURL; |
michael@0 | 317 | |
michael@0 | 318 | NS_ASSERTION(mPluginInstance == nullptr, |
michael@0 | 319 | "nsPluginStreamListenerPeer::Initialize mPluginInstance != nullptr"); |
michael@0 | 320 | mPluginInstance = aInstance; |
michael@0 | 321 | |
michael@0 | 322 | // If the plugin did not request this stream, e.g. the initial stream, we wont |
michael@0 | 323 | // have a nsNPAPIPluginStreamListener yet - this will be handled by |
michael@0 | 324 | // SetUpStreamListener |
michael@0 | 325 | if (aListener) { |
michael@0 | 326 | mPStreamListener = aListener; |
michael@0 | 327 | mPStreamListener->SetStreamListenerPeer(this); |
michael@0 | 328 | } |
michael@0 | 329 | |
michael@0 | 330 | mPendingRequests = 1; |
michael@0 | 331 | |
michael@0 | 332 | mDataForwardToRequest = new nsDataHashtable<nsUint32HashKey, uint32_t>(); |
michael@0 | 333 | |
michael@0 | 334 | return NS_OK; |
michael@0 | 335 | } |
michael@0 | 336 | |
michael@0 | 337 | // SetupPluginCacheFile is called if we have to save the stream to disk. |
michael@0 | 338 | // |
michael@0 | 339 | // These files will be deleted when the host is destroyed. |
michael@0 | 340 | // |
michael@0 | 341 | // TODO? What if we fill up the the dest dir? |
michael@0 | 342 | nsresult |
michael@0 | 343 | nsPluginStreamListenerPeer::SetupPluginCacheFile(nsIChannel* channel) |
michael@0 | 344 | { |
michael@0 | 345 | nsresult rv = NS_OK; |
michael@0 | 346 | |
michael@0 | 347 | bool useExistingCacheFile = false; |
michael@0 | 348 | nsRefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst(); |
michael@0 | 349 | |
michael@0 | 350 | // Look for an existing cache file for the URI. |
michael@0 | 351 | nsTArray< nsRefPtr<nsNPAPIPluginInstance> > *instances = pluginHost->InstanceArray(); |
michael@0 | 352 | for (uint32_t i = 0; i < instances->Length(); i++) { |
michael@0 | 353 | // most recent streams are at the end of list |
michael@0 | 354 | nsTArray<nsPluginStreamListenerPeer*> *streamListeners = instances->ElementAt(i)->FileCachedStreamListeners(); |
michael@0 | 355 | for (int32_t i = streamListeners->Length() - 1; i >= 0; --i) { |
michael@0 | 356 | nsPluginStreamListenerPeer *lp = streamListeners->ElementAt(i); |
michael@0 | 357 | if (lp && lp->mLocalCachedFileHolder) { |
michael@0 | 358 | useExistingCacheFile = lp->UseExistingPluginCacheFile(this); |
michael@0 | 359 | if (useExistingCacheFile) { |
michael@0 | 360 | mLocalCachedFileHolder = lp->mLocalCachedFileHolder; |
michael@0 | 361 | break; |
michael@0 | 362 | } |
michael@0 | 363 | } |
michael@0 | 364 | if (useExistingCacheFile) |
michael@0 | 365 | break; |
michael@0 | 366 | } |
michael@0 | 367 | } |
michael@0 | 368 | |
michael@0 | 369 | // Create a new cache file if one could not be found. |
michael@0 | 370 | if (!useExistingCacheFile) { |
michael@0 | 371 | nsCOMPtr<nsIFile> pluginTmp; |
michael@0 | 372 | rv = nsPluginHost::GetPluginTempDir(getter_AddRefs(pluginTmp)); |
michael@0 | 373 | if (NS_FAILED(rv)) { |
michael@0 | 374 | return rv; |
michael@0 | 375 | } |
michael@0 | 376 | |
michael@0 | 377 | // Get the filename from the channel |
michael@0 | 378 | nsCOMPtr<nsIURI> uri; |
michael@0 | 379 | rv = channel->GetURI(getter_AddRefs(uri)); |
michael@0 | 380 | if (NS_FAILED(rv)) return rv; |
michael@0 | 381 | |
michael@0 | 382 | nsCOMPtr<nsIURL> url(do_QueryInterface(uri)); |
michael@0 | 383 | if (!url) |
michael@0 | 384 | return NS_ERROR_FAILURE; |
michael@0 | 385 | |
michael@0 | 386 | nsAutoCString filename; |
michael@0 | 387 | url->GetFileName(filename); |
michael@0 | 388 | if (NS_FAILED(rv)) |
michael@0 | 389 | return rv; |
michael@0 | 390 | |
michael@0 | 391 | // Create a file to save our stream into. Should we scramble the name? |
michael@0 | 392 | filename.Insert(NS_LITERAL_CSTRING("plugin-"), 0); |
michael@0 | 393 | rv = pluginTmp->AppendNative(filename); |
michael@0 | 394 | if (NS_FAILED(rv)) |
michael@0 | 395 | return rv; |
michael@0 | 396 | |
michael@0 | 397 | // Yes, make it unique. |
michael@0 | 398 | rv = pluginTmp->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600); |
michael@0 | 399 | if (NS_FAILED(rv)) |
michael@0 | 400 | return rv; |
michael@0 | 401 | |
michael@0 | 402 | // create a file output stream to write to... |
michael@0 | 403 | nsCOMPtr<nsIOutputStream> outstream; |
michael@0 | 404 | rv = NS_NewLocalFileOutputStream(getter_AddRefs(mFileCacheOutputStream), pluginTmp, -1, 00600); |
michael@0 | 405 | if (NS_FAILED(rv)) |
michael@0 | 406 | return rv; |
michael@0 | 407 | |
michael@0 | 408 | // save the file. |
michael@0 | 409 | mLocalCachedFileHolder = new CachedFileHolder(pluginTmp); |
michael@0 | 410 | } |
michael@0 | 411 | |
michael@0 | 412 | // add this listenerPeer to list of stream peers for this instance |
michael@0 | 413 | mPluginInstance->FileCachedStreamListeners()->AppendElement(this); |
michael@0 | 414 | |
michael@0 | 415 | return rv; |
michael@0 | 416 | } |
michael@0 | 417 | |
michael@0 | 418 | NS_IMETHODIMP |
michael@0 | 419 | nsPluginStreamListenerPeer::OnStartRequest(nsIRequest *request, |
michael@0 | 420 | nsISupports* aContext) |
michael@0 | 421 | { |
michael@0 | 422 | nsresult rv = NS_OK; |
michael@0 | 423 | PROFILER_LABEL("nsPluginStreamListenerPeer", "OnStartRequest"); |
michael@0 | 424 | |
michael@0 | 425 | if (mRequests.IndexOfObject(GetBaseRequest(request)) == -1) { |
michael@0 | 426 | NS_ASSERTION(mRequests.Count() == 0, |
michael@0 | 427 | "Only our initial stream should be unknown!"); |
michael@0 | 428 | TrackRequest(request); |
michael@0 | 429 | } |
michael@0 | 430 | |
michael@0 | 431 | if (mHaveFiredOnStartRequest) { |
michael@0 | 432 | return NS_OK; |
michael@0 | 433 | } |
michael@0 | 434 | |
michael@0 | 435 | mHaveFiredOnStartRequest = true; |
michael@0 | 436 | |
michael@0 | 437 | nsCOMPtr<nsIChannel> channel = do_QueryInterface(request); |
michael@0 | 438 | NS_ENSURE_TRUE(channel, NS_ERROR_FAILURE); |
michael@0 | 439 | |
michael@0 | 440 | // deal with 404 (Not Found) HTTP response, |
michael@0 | 441 | // just return, this causes the request to be ignored. |
michael@0 | 442 | nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel)); |
michael@0 | 443 | if (httpChannel) { |
michael@0 | 444 | uint32_t responseCode = 0; |
michael@0 | 445 | rv = httpChannel->GetResponseStatus(&responseCode); |
michael@0 | 446 | if (NS_FAILED(rv)) { |
michael@0 | 447 | // NPP_Notify() will be called from OnStopRequest |
michael@0 | 448 | // in nsNPAPIPluginStreamListener::CleanUpStream |
michael@0 | 449 | // return error will cancel this request |
michael@0 | 450 | // ...and we also need to tell the plugin that |
michael@0 | 451 | mRequestFailed = true; |
michael@0 | 452 | return NS_ERROR_FAILURE; |
michael@0 | 453 | } |
michael@0 | 454 | |
michael@0 | 455 | if (responseCode > 206) { // not normal |
michael@0 | 456 | uint32_t wantsAllNetworkStreams = 0; |
michael@0 | 457 | |
michael@0 | 458 | // We don't always have an instance here already, but if we do, check |
michael@0 | 459 | // to see if it wants all streams. |
michael@0 | 460 | if (mPluginInstance) { |
michael@0 | 461 | rv = mPluginInstance->GetValueFromPlugin(NPPVpluginWantsAllNetworkStreams, |
michael@0 | 462 | &wantsAllNetworkStreams); |
michael@0 | 463 | // If the call returned an error code make sure we still use our default value. |
michael@0 | 464 | if (NS_FAILED(rv)) { |
michael@0 | 465 | wantsAllNetworkStreams = 0; |
michael@0 | 466 | } |
michael@0 | 467 | } |
michael@0 | 468 | |
michael@0 | 469 | if (!wantsAllNetworkStreams) { |
michael@0 | 470 | mRequestFailed = true; |
michael@0 | 471 | return NS_ERROR_FAILURE; |
michael@0 | 472 | } |
michael@0 | 473 | } |
michael@0 | 474 | } |
michael@0 | 475 | |
michael@0 | 476 | // Get the notification callbacks from the channel and save it as |
michael@0 | 477 | // week ref we'll use it in nsPluginStreamInfo::RequestRead() when |
michael@0 | 478 | // we'll create channel for byte range request. |
michael@0 | 479 | nsCOMPtr<nsIInterfaceRequestor> callbacks; |
michael@0 | 480 | channel->GetNotificationCallbacks(getter_AddRefs(callbacks)); |
michael@0 | 481 | if (callbacks) |
michael@0 | 482 | mWeakPtrChannelCallbacks = do_GetWeakReference(callbacks); |
michael@0 | 483 | |
michael@0 | 484 | nsCOMPtr<nsILoadGroup> loadGroup; |
michael@0 | 485 | channel->GetLoadGroup(getter_AddRefs(loadGroup)); |
michael@0 | 486 | if (loadGroup) |
michael@0 | 487 | mWeakPtrChannelLoadGroup = do_GetWeakReference(loadGroup); |
michael@0 | 488 | |
michael@0 | 489 | int64_t length; |
michael@0 | 490 | rv = channel->GetContentLength(&length); |
michael@0 | 491 | |
michael@0 | 492 | // it's possible for the server to not send a Content-Length. |
michael@0 | 493 | // we should still work in this case. |
michael@0 | 494 | if (NS_FAILED(rv) || length < 0 || length > UINT32_MAX) { |
michael@0 | 495 | // check out if this is file channel |
michael@0 | 496 | nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(channel); |
michael@0 | 497 | if (fileChannel) { |
michael@0 | 498 | // file does not exist |
michael@0 | 499 | mRequestFailed = true; |
michael@0 | 500 | return NS_ERROR_FAILURE; |
michael@0 | 501 | } |
michael@0 | 502 | mLength = 0; |
michael@0 | 503 | } |
michael@0 | 504 | else { |
michael@0 | 505 | mLength = uint32_t(length); |
michael@0 | 506 | } |
michael@0 | 507 | |
michael@0 | 508 | nsAutoCString aContentType; // XXX but we already got the type above! |
michael@0 | 509 | rv = channel->GetContentType(aContentType); |
michael@0 | 510 | if (NS_FAILED(rv)) |
michael@0 | 511 | return rv; |
michael@0 | 512 | |
michael@0 | 513 | nsCOMPtr<nsIURI> aURL; |
michael@0 | 514 | rv = channel->GetURI(getter_AddRefs(aURL)); |
michael@0 | 515 | if (NS_FAILED(rv)) |
michael@0 | 516 | return rv; |
michael@0 | 517 | |
michael@0 | 518 | aURL->GetSpec(mURLSpec); |
michael@0 | 519 | |
michael@0 | 520 | if (!aContentType.IsEmpty()) |
michael@0 | 521 | mContentType = aContentType; |
michael@0 | 522 | |
michael@0 | 523 | #ifdef PLUGIN_LOGGING |
michael@0 | 524 | PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NOISY, |
michael@0 | 525 | ("nsPluginStreamListenerPeer::OnStartRequest this=%p request=%p mime=%s, url=%s\n", |
michael@0 | 526 | this, request, aContentType.get(), mURLSpec.get())); |
michael@0 | 527 | |
michael@0 | 528 | PR_LogFlush(); |
michael@0 | 529 | #endif |
michael@0 | 530 | |
michael@0 | 531 | // Set up the stream listener... |
michael@0 | 532 | rv = SetUpStreamListener(request, aURL); |
michael@0 | 533 | if (NS_FAILED(rv)) return rv; |
michael@0 | 534 | |
michael@0 | 535 | return rv; |
michael@0 | 536 | } |
michael@0 | 537 | |
michael@0 | 538 | NS_IMETHODIMP nsPluginStreamListenerPeer::OnProgress(nsIRequest *request, |
michael@0 | 539 | nsISupports* aContext, |
michael@0 | 540 | uint64_t aProgress, |
michael@0 | 541 | uint64_t aProgressMax) |
michael@0 | 542 | { |
michael@0 | 543 | nsresult rv = NS_OK; |
michael@0 | 544 | return rv; |
michael@0 | 545 | } |
michael@0 | 546 | |
michael@0 | 547 | NS_IMETHODIMP nsPluginStreamListenerPeer::OnStatus(nsIRequest *request, |
michael@0 | 548 | nsISupports* aContext, |
michael@0 | 549 | nsresult aStatus, |
michael@0 | 550 | const char16_t* aStatusArg) |
michael@0 | 551 | { |
michael@0 | 552 | return NS_OK; |
michael@0 | 553 | } |
michael@0 | 554 | |
michael@0 | 555 | nsresult |
michael@0 | 556 | nsPluginStreamListenerPeer::GetContentType(char** result) |
michael@0 | 557 | { |
michael@0 | 558 | *result = const_cast<char*>(mContentType.get()); |
michael@0 | 559 | return NS_OK; |
michael@0 | 560 | } |
michael@0 | 561 | |
michael@0 | 562 | |
michael@0 | 563 | nsresult |
michael@0 | 564 | nsPluginStreamListenerPeer::IsSeekable(bool* result) |
michael@0 | 565 | { |
michael@0 | 566 | *result = mSeekable; |
michael@0 | 567 | return NS_OK; |
michael@0 | 568 | } |
michael@0 | 569 | |
michael@0 | 570 | nsresult |
michael@0 | 571 | nsPluginStreamListenerPeer::GetLength(uint32_t* result) |
michael@0 | 572 | { |
michael@0 | 573 | *result = mLength; |
michael@0 | 574 | return NS_OK; |
michael@0 | 575 | } |
michael@0 | 576 | |
michael@0 | 577 | nsresult |
michael@0 | 578 | nsPluginStreamListenerPeer::GetLastModified(uint32_t* result) |
michael@0 | 579 | { |
michael@0 | 580 | *result = mModified; |
michael@0 | 581 | return NS_OK; |
michael@0 | 582 | } |
michael@0 | 583 | |
michael@0 | 584 | nsresult |
michael@0 | 585 | nsPluginStreamListenerPeer::GetURL(const char** result) |
michael@0 | 586 | { |
michael@0 | 587 | *result = mURLSpec.get(); |
michael@0 | 588 | return NS_OK; |
michael@0 | 589 | } |
michael@0 | 590 | |
michael@0 | 591 | void |
michael@0 | 592 | nsPluginStreamListenerPeer::MakeByteRangeString(NPByteRange* aRangeList, nsACString &rangeRequest, |
michael@0 | 593 | int32_t *numRequests) |
michael@0 | 594 | { |
michael@0 | 595 | rangeRequest.Truncate(); |
michael@0 | 596 | *numRequests = 0; |
michael@0 | 597 | //the string should look like this: bytes=500-700,601-999 |
michael@0 | 598 | if (!aRangeList) |
michael@0 | 599 | return; |
michael@0 | 600 | |
michael@0 | 601 | int32_t requestCnt = 0; |
michael@0 | 602 | nsAutoCString string("bytes="); |
michael@0 | 603 | |
michael@0 | 604 | for (NPByteRange * range = aRangeList; range != nullptr; range = range->next) { |
michael@0 | 605 | // XXX zero length? |
michael@0 | 606 | if (!range->length) |
michael@0 | 607 | continue; |
michael@0 | 608 | |
michael@0 | 609 | // XXX needs to be fixed for negative offsets |
michael@0 | 610 | string.AppendInt(range->offset); |
michael@0 | 611 | string.Append("-"); |
michael@0 | 612 | string.AppendInt(range->offset + range->length - 1); |
michael@0 | 613 | if (range->next) |
michael@0 | 614 | string += ","; |
michael@0 | 615 | |
michael@0 | 616 | requestCnt++; |
michael@0 | 617 | } |
michael@0 | 618 | |
michael@0 | 619 | // get rid of possible trailing comma |
michael@0 | 620 | string.Trim(",", false); |
michael@0 | 621 | |
michael@0 | 622 | rangeRequest = string; |
michael@0 | 623 | *numRequests = requestCnt; |
michael@0 | 624 | return; |
michael@0 | 625 | } |
michael@0 | 626 | |
michael@0 | 627 | nsresult |
michael@0 | 628 | nsPluginStreamListenerPeer::RequestRead(NPByteRange* rangeList) |
michael@0 | 629 | { |
michael@0 | 630 | nsAutoCString rangeString; |
michael@0 | 631 | int32_t numRequests; |
michael@0 | 632 | |
michael@0 | 633 | MakeByteRangeString(rangeList, rangeString, &numRequests); |
michael@0 | 634 | |
michael@0 | 635 | if (numRequests == 0) |
michael@0 | 636 | return NS_ERROR_FAILURE; |
michael@0 | 637 | |
michael@0 | 638 | nsresult rv = NS_OK; |
michael@0 | 639 | |
michael@0 | 640 | nsCOMPtr<nsIInterfaceRequestor> callbacks = do_QueryReferent(mWeakPtrChannelCallbacks); |
michael@0 | 641 | nsCOMPtr<nsILoadGroup> loadGroup = do_QueryReferent(mWeakPtrChannelLoadGroup); |
michael@0 | 642 | nsCOMPtr<nsIChannel> channel; |
michael@0 | 643 | rv = NS_NewChannel(getter_AddRefs(channel), mURL, nullptr, loadGroup, callbacks); |
michael@0 | 644 | if (NS_FAILED(rv)) |
michael@0 | 645 | return rv; |
michael@0 | 646 | |
michael@0 | 647 | nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel)); |
michael@0 | 648 | if (!httpChannel) |
michael@0 | 649 | return NS_ERROR_FAILURE; |
michael@0 | 650 | |
michael@0 | 651 | httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString, false); |
michael@0 | 652 | |
michael@0 | 653 | mAbort = true; // instruct old stream listener to cancel |
michael@0 | 654 | // the request on the next ODA. |
michael@0 | 655 | |
michael@0 | 656 | nsCOMPtr<nsIStreamListener> converter; |
michael@0 | 657 | |
michael@0 | 658 | if (numRequests == 1) { |
michael@0 | 659 | converter = this; |
michael@0 | 660 | // set current stream offset equal to the first offset in the range list |
michael@0 | 661 | // it will work for single byte range request |
michael@0 | 662 | // for multy range we'll reset it in ODA |
michael@0 | 663 | SetStreamOffset(rangeList->offset); |
michael@0 | 664 | } else { |
michael@0 | 665 | nsWeakPtr weakpeer = |
michael@0 | 666 | do_GetWeakReference(static_cast<nsISupportsWeakReference*>(this)); |
michael@0 | 667 | nsPluginByteRangeStreamListener *brrListener = |
michael@0 | 668 | new nsPluginByteRangeStreamListener(weakpeer); |
michael@0 | 669 | if (brrListener) |
michael@0 | 670 | converter = brrListener; |
michael@0 | 671 | else |
michael@0 | 672 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 673 | } |
michael@0 | 674 | |
michael@0 | 675 | mPendingRequests += numRequests; |
michael@0 | 676 | |
michael@0 | 677 | nsCOMPtr<nsISupportsPRUint32> container = do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv); |
michael@0 | 678 | if (NS_FAILED(rv)) |
michael@0 | 679 | return rv; |
michael@0 | 680 | rv = container->SetData(MAGIC_REQUEST_CONTEXT); |
michael@0 | 681 | if (NS_FAILED(rv)) |
michael@0 | 682 | return rv; |
michael@0 | 683 | |
michael@0 | 684 | rv = channel->AsyncOpen(converter, container); |
michael@0 | 685 | if (NS_SUCCEEDED(rv)) |
michael@0 | 686 | TrackRequest(channel); |
michael@0 | 687 | return rv; |
michael@0 | 688 | } |
michael@0 | 689 | |
michael@0 | 690 | nsresult |
michael@0 | 691 | nsPluginStreamListenerPeer::GetStreamOffset(int32_t* result) |
michael@0 | 692 | { |
michael@0 | 693 | *result = mStreamOffset; |
michael@0 | 694 | return NS_OK; |
michael@0 | 695 | } |
michael@0 | 696 | |
michael@0 | 697 | nsresult |
michael@0 | 698 | nsPluginStreamListenerPeer::SetStreamOffset(int32_t value) |
michael@0 | 699 | { |
michael@0 | 700 | mStreamOffset = value; |
michael@0 | 701 | return NS_OK; |
michael@0 | 702 | } |
michael@0 | 703 | |
michael@0 | 704 | nsresult nsPluginStreamListenerPeer::ServeStreamAsFile(nsIRequest *request, |
michael@0 | 705 | nsISupports* aContext) |
michael@0 | 706 | { |
michael@0 | 707 | if (!mPluginInstance) |
michael@0 | 708 | return NS_ERROR_FAILURE; |
michael@0 | 709 | |
michael@0 | 710 | // mPluginInstance->Stop calls mPStreamListener->CleanUpStream(), so stream will be properly clean up |
michael@0 | 711 | mPluginInstance->Stop(); |
michael@0 | 712 | mPluginInstance->Start(); |
michael@0 | 713 | nsRefPtr<nsPluginInstanceOwner> owner = mPluginInstance->GetOwner(); |
michael@0 | 714 | if (owner) { |
michael@0 | 715 | NPWindow* window = nullptr; |
michael@0 | 716 | owner->GetWindow(window); |
michael@0 | 717 | #if (MOZ_WIDGET_GTK == 2) || defined(MOZ_WIDGET_QT) |
michael@0 | 718 | // Should call GetPluginPort() here. |
michael@0 | 719 | // This part is copied from nsPluginInstanceOwner::GetPluginPort(). |
michael@0 | 720 | nsCOMPtr<nsIWidget> widget; |
michael@0 | 721 | ((nsPluginNativeWindow*)window)->GetPluginWidget(getter_AddRefs(widget)); |
michael@0 | 722 | if (widget) { |
michael@0 | 723 | window->window = widget->GetNativeData(NS_NATIVE_PLUGIN_PORT); |
michael@0 | 724 | } |
michael@0 | 725 | #endif |
michael@0 | 726 | owner->CallSetWindow(); |
michael@0 | 727 | } |
michael@0 | 728 | |
michael@0 | 729 | mSeekable = false; |
michael@0 | 730 | mPStreamListener->OnStartBinding(this); |
michael@0 | 731 | mStreamOffset = 0; |
michael@0 | 732 | |
michael@0 | 733 | // force the plugin to use stream as file |
michael@0 | 734 | mStreamType = NP_ASFILE; |
michael@0 | 735 | |
michael@0 | 736 | nsCOMPtr<nsIChannel> channel = do_QueryInterface(request); |
michael@0 | 737 | if (channel) { |
michael@0 | 738 | SetupPluginCacheFile(channel); |
michael@0 | 739 | } |
michael@0 | 740 | |
michael@0 | 741 | // unset mPendingRequests |
michael@0 | 742 | mPendingRequests = 0; |
michael@0 | 743 | |
michael@0 | 744 | return NS_OK; |
michael@0 | 745 | } |
michael@0 | 746 | |
michael@0 | 747 | bool |
michael@0 | 748 | nsPluginStreamListenerPeer::UseExistingPluginCacheFile(nsPluginStreamListenerPeer* psi) |
michael@0 | 749 | { |
michael@0 | 750 | NS_ENSURE_TRUE(psi, false); |
michael@0 | 751 | |
michael@0 | 752 | if (psi->mLength == mLength && |
michael@0 | 753 | psi->mModified == mModified && |
michael@0 | 754 | mStreamComplete && |
michael@0 | 755 | mURLSpec.Equals(psi->mURLSpec)) |
michael@0 | 756 | { |
michael@0 | 757 | return true; |
michael@0 | 758 | } |
michael@0 | 759 | return false; |
michael@0 | 760 | } |
michael@0 | 761 | |
michael@0 | 762 | NS_IMETHODIMP nsPluginStreamListenerPeer::OnDataAvailable(nsIRequest *request, |
michael@0 | 763 | nsISupports* aContext, |
michael@0 | 764 | nsIInputStream *aIStream, |
michael@0 | 765 | uint64_t sourceOffset, |
michael@0 | 766 | uint32_t aLength) |
michael@0 | 767 | { |
michael@0 | 768 | if (mRequests.IndexOfObject(GetBaseRequest(request)) == -1) { |
michael@0 | 769 | MOZ_ASSERT(false, "Received OnDataAvailable for untracked request."); |
michael@0 | 770 | return NS_ERROR_UNEXPECTED; |
michael@0 | 771 | } |
michael@0 | 772 | |
michael@0 | 773 | if (mRequestFailed) |
michael@0 | 774 | return NS_ERROR_FAILURE; |
michael@0 | 775 | |
michael@0 | 776 | if (mAbort) { |
michael@0 | 777 | uint32_t magicNumber = 0; // set it to something that is not the magic number. |
michael@0 | 778 | nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(aContext); |
michael@0 | 779 | if (container) |
michael@0 | 780 | container->GetData(&magicNumber); |
michael@0 | 781 | |
michael@0 | 782 | if (magicNumber != MAGIC_REQUEST_CONTEXT) { |
michael@0 | 783 | // this is not one of our range requests |
michael@0 | 784 | mAbort = false; |
michael@0 | 785 | return NS_BINDING_ABORTED; |
michael@0 | 786 | } |
michael@0 | 787 | } |
michael@0 | 788 | |
michael@0 | 789 | nsresult rv = NS_OK; |
michael@0 | 790 | |
michael@0 | 791 | if (!mPStreamListener) |
michael@0 | 792 | return NS_ERROR_FAILURE; |
michael@0 | 793 | |
michael@0 | 794 | const char * url = nullptr; |
michael@0 | 795 | GetURL(&url); |
michael@0 | 796 | |
michael@0 | 797 | PLUGIN_LOG(PLUGIN_LOG_NOISY, |
michael@0 | 798 | ("nsPluginStreamListenerPeer::OnDataAvailable this=%p request=%p, offset=%llu, length=%u, url=%s\n", |
michael@0 | 799 | this, request, sourceOffset, aLength, url ? url : "no url set")); |
michael@0 | 800 | |
michael@0 | 801 | // if the plugin has requested an AsFileOnly stream, then don't |
michael@0 | 802 | // call OnDataAvailable |
michael@0 | 803 | if (mStreamType != NP_ASFILEONLY) { |
michael@0 | 804 | // get the absolute offset of the request, if one exists. |
michael@0 | 805 | nsCOMPtr<nsIByteRangeRequest> brr = do_QueryInterface(request); |
michael@0 | 806 | if (brr) { |
michael@0 | 807 | if (!mDataForwardToRequest) |
michael@0 | 808 | return NS_ERROR_FAILURE; |
michael@0 | 809 | |
michael@0 | 810 | int64_t absoluteOffset64 = 0; |
michael@0 | 811 | brr->GetStartRange(&absoluteOffset64); |
michael@0 | 812 | |
michael@0 | 813 | // XXX handle 64-bit for real |
michael@0 | 814 | int32_t absoluteOffset = (int32_t)int64_t(absoluteOffset64); |
michael@0 | 815 | |
michael@0 | 816 | // we need to track how much data we have forwarded to the |
michael@0 | 817 | // plugin. |
michael@0 | 818 | |
michael@0 | 819 | // FIXME: http://bugzilla.mozilla.org/show_bug.cgi?id=240130 |
michael@0 | 820 | // |
michael@0 | 821 | // Why couldn't this be tracked on the plugin info, and not in a |
michael@0 | 822 | // *hash table*? |
michael@0 | 823 | int32_t amtForwardToPlugin = mDataForwardToRequest->Get(absoluteOffset); |
michael@0 | 824 | mDataForwardToRequest->Put(absoluteOffset, (amtForwardToPlugin + aLength)); |
michael@0 | 825 | |
michael@0 | 826 | SetStreamOffset(absoluteOffset + amtForwardToPlugin); |
michael@0 | 827 | } |
michael@0 | 828 | |
michael@0 | 829 | nsCOMPtr<nsIInputStream> stream = aIStream; |
michael@0 | 830 | |
michael@0 | 831 | // if we are caching the file ourselves to disk, we want to 'tee' off |
michael@0 | 832 | // the data as the plugin read from the stream. We do this by the magic |
michael@0 | 833 | // of an input stream tee. |
michael@0 | 834 | |
michael@0 | 835 | if (mFileCacheOutputStream) { |
michael@0 | 836 | rv = NS_NewInputStreamTee(getter_AddRefs(stream), aIStream, mFileCacheOutputStream); |
michael@0 | 837 | if (NS_FAILED(rv)) |
michael@0 | 838 | return rv; |
michael@0 | 839 | } |
michael@0 | 840 | |
michael@0 | 841 | rv = mPStreamListener->OnDataAvailable(this, |
michael@0 | 842 | stream, |
michael@0 | 843 | aLength); |
michael@0 | 844 | |
michael@0 | 845 | // if a plugin returns an error, the peer must kill the stream |
michael@0 | 846 | // else the stream and PluginStreamListener leak |
michael@0 | 847 | if (NS_FAILED(rv)) |
michael@0 | 848 | request->Cancel(rv); |
michael@0 | 849 | } |
michael@0 | 850 | else |
michael@0 | 851 | { |
michael@0 | 852 | // if we don't read from the stream, OnStopRequest will never be called |
michael@0 | 853 | char* buffer = new char[aLength]; |
michael@0 | 854 | uint32_t amountRead, amountWrote = 0; |
michael@0 | 855 | rv = aIStream->Read(buffer, aLength, &amountRead); |
michael@0 | 856 | |
michael@0 | 857 | // if we are caching this to disk ourselves, lets write the bytes out. |
michael@0 | 858 | if (mFileCacheOutputStream) { |
michael@0 | 859 | while (amountWrote < amountRead && NS_SUCCEEDED(rv)) { |
michael@0 | 860 | rv = mFileCacheOutputStream->Write(buffer, amountRead, &amountWrote); |
michael@0 | 861 | } |
michael@0 | 862 | } |
michael@0 | 863 | delete [] buffer; |
michael@0 | 864 | } |
michael@0 | 865 | return rv; |
michael@0 | 866 | } |
michael@0 | 867 | |
michael@0 | 868 | NS_IMETHODIMP nsPluginStreamListenerPeer::OnStopRequest(nsIRequest *request, |
michael@0 | 869 | nsISupports* aContext, |
michael@0 | 870 | nsresult aStatus) |
michael@0 | 871 | { |
michael@0 | 872 | nsresult rv = NS_OK; |
michael@0 | 873 | |
michael@0 | 874 | nsCOMPtr<nsIMultiPartChannel> mp = do_QueryInterface(request); |
michael@0 | 875 | if (!mp) { |
michael@0 | 876 | bool found = mRequests.RemoveObject(request); |
michael@0 | 877 | if (!found) { |
michael@0 | 878 | NS_ERROR("Received OnStopRequest for untracked request."); |
michael@0 | 879 | } |
michael@0 | 880 | } |
michael@0 | 881 | |
michael@0 | 882 | PLUGIN_LOG(PLUGIN_LOG_NOISY, |
michael@0 | 883 | ("nsPluginStreamListenerPeer::OnStopRequest this=%p aStatus=%d request=%p\n", |
michael@0 | 884 | this, aStatus, request)); |
michael@0 | 885 | |
michael@0 | 886 | // for ByteRangeRequest we're just updating the mDataForwardToRequest hash and return. |
michael@0 | 887 | nsCOMPtr<nsIByteRangeRequest> brr = do_QueryInterface(request); |
michael@0 | 888 | if (brr) { |
michael@0 | 889 | int64_t absoluteOffset64 = 0; |
michael@0 | 890 | brr->GetStartRange(&absoluteOffset64); |
michael@0 | 891 | // XXX support 64-bit offsets |
michael@0 | 892 | int32_t absoluteOffset = (int32_t)int64_t(absoluteOffset64); |
michael@0 | 893 | |
michael@0 | 894 | // remove the request from our data forwarding count hash. |
michael@0 | 895 | mDataForwardToRequest->Remove(absoluteOffset); |
michael@0 | 896 | |
michael@0 | 897 | |
michael@0 | 898 | PLUGIN_LOG(PLUGIN_LOG_NOISY, |
michael@0 | 899 | (" ::OnStopRequest for ByteRangeRequest Started=%d\n", |
michael@0 | 900 | absoluteOffset)); |
michael@0 | 901 | } else { |
michael@0 | 902 | // if this is not byte range request and |
michael@0 | 903 | // if we are writting the stream to disk ourselves, |
michael@0 | 904 | // close & tear it down here |
michael@0 | 905 | mFileCacheOutputStream = nullptr; |
michael@0 | 906 | } |
michael@0 | 907 | |
michael@0 | 908 | // if we still have pending stuff to do, lets not close the plugin socket. |
michael@0 | 909 | if (--mPendingRequests > 0) |
michael@0 | 910 | return NS_OK; |
michael@0 | 911 | |
michael@0 | 912 | // we keep our connections around... |
michael@0 | 913 | nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(aContext); |
michael@0 | 914 | if (container) { |
michael@0 | 915 | uint32_t magicNumber = 0; // set it to something that is not the magic number. |
michael@0 | 916 | container->GetData(&magicNumber); |
michael@0 | 917 | if (magicNumber == MAGIC_REQUEST_CONTEXT) { |
michael@0 | 918 | // this is one of our range requests |
michael@0 | 919 | return NS_OK; |
michael@0 | 920 | } |
michael@0 | 921 | } |
michael@0 | 922 | |
michael@0 | 923 | if (!mPStreamListener) |
michael@0 | 924 | return NS_ERROR_FAILURE; |
michael@0 | 925 | |
michael@0 | 926 | nsCOMPtr<nsIChannel> channel = do_QueryInterface(request); |
michael@0 | 927 | if (!channel) |
michael@0 | 928 | return NS_ERROR_FAILURE; |
michael@0 | 929 | // Set the content type to ensure we don't pass null to the plugin |
michael@0 | 930 | nsAutoCString aContentType; |
michael@0 | 931 | rv = channel->GetContentType(aContentType); |
michael@0 | 932 | if (NS_FAILED(rv) && !mRequestFailed) |
michael@0 | 933 | return rv; |
michael@0 | 934 | |
michael@0 | 935 | if (!aContentType.IsEmpty()) |
michael@0 | 936 | mContentType = aContentType; |
michael@0 | 937 | |
michael@0 | 938 | // set error status if stream failed so we notify the plugin |
michael@0 | 939 | if (mRequestFailed) |
michael@0 | 940 | aStatus = NS_ERROR_FAILURE; |
michael@0 | 941 | |
michael@0 | 942 | if (NS_FAILED(aStatus)) { |
michael@0 | 943 | // on error status cleanup the stream |
michael@0 | 944 | // and return w/o OnFileAvailable() |
michael@0 | 945 | mPStreamListener->OnStopBinding(this, aStatus); |
michael@0 | 946 | return NS_OK; |
michael@0 | 947 | } |
michael@0 | 948 | |
michael@0 | 949 | // call OnFileAvailable if plugin requests stream type StreamType_AsFile or StreamType_AsFileOnly |
michael@0 | 950 | if (mStreamType >= NP_ASFILE) { |
michael@0 | 951 | nsCOMPtr<nsIFile> localFile; |
michael@0 | 952 | if (mLocalCachedFileHolder) |
michael@0 | 953 | localFile = mLocalCachedFileHolder->file(); |
michael@0 | 954 | else { |
michael@0 | 955 | // see if it is a file channel. |
michael@0 | 956 | nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(request); |
michael@0 | 957 | if (fileChannel) { |
michael@0 | 958 | fileChannel->GetFile(getter_AddRefs(localFile)); |
michael@0 | 959 | } |
michael@0 | 960 | } |
michael@0 | 961 | |
michael@0 | 962 | if (localFile) { |
michael@0 | 963 | OnFileAvailable(localFile); |
michael@0 | 964 | } |
michael@0 | 965 | } |
michael@0 | 966 | |
michael@0 | 967 | if (mStartBinding) { |
michael@0 | 968 | // On start binding has been called |
michael@0 | 969 | mPStreamListener->OnStopBinding(this, aStatus); |
michael@0 | 970 | } else { |
michael@0 | 971 | // OnStartBinding hasn't been called, so complete the action. |
michael@0 | 972 | mPStreamListener->OnStartBinding(this); |
michael@0 | 973 | mPStreamListener->OnStopBinding(this, aStatus); |
michael@0 | 974 | } |
michael@0 | 975 | |
michael@0 | 976 | if (NS_SUCCEEDED(aStatus)) { |
michael@0 | 977 | mStreamComplete = true; |
michael@0 | 978 | } |
michael@0 | 979 | |
michael@0 | 980 | return NS_OK; |
michael@0 | 981 | } |
michael@0 | 982 | |
michael@0 | 983 | nsresult nsPluginStreamListenerPeer::SetUpStreamListener(nsIRequest *request, |
michael@0 | 984 | nsIURI* aURL) |
michael@0 | 985 | { |
michael@0 | 986 | nsresult rv = NS_OK; |
michael@0 | 987 | |
michael@0 | 988 | // If we don't yet have a stream listener, we need to get |
michael@0 | 989 | // one from the plugin. |
michael@0 | 990 | // NOTE: this should only happen when a stream was NOT created |
michael@0 | 991 | // with GetURL or PostURL (i.e. it's the initial stream we |
michael@0 | 992 | // send to the plugin as determined by the SRC or DATA attribute) |
michael@0 | 993 | if (!mPStreamListener) { |
michael@0 | 994 | if (!mPluginInstance) { |
michael@0 | 995 | return NS_ERROR_FAILURE; |
michael@0 | 996 | } |
michael@0 | 997 | |
michael@0 | 998 | nsRefPtr<nsNPAPIPluginStreamListener> streamListener; |
michael@0 | 999 | rv = mPluginInstance->NewStreamListener(nullptr, nullptr, |
michael@0 | 1000 | getter_AddRefs(streamListener)); |
michael@0 | 1001 | if (NS_FAILED(rv) || !streamListener) { |
michael@0 | 1002 | return NS_ERROR_FAILURE; |
michael@0 | 1003 | } |
michael@0 | 1004 | |
michael@0 | 1005 | mPStreamListener = static_cast<nsNPAPIPluginStreamListener*>(streamListener.get()); |
michael@0 | 1006 | } |
michael@0 | 1007 | |
michael@0 | 1008 | mPStreamListener->SetStreamListenerPeer(this); |
michael@0 | 1009 | |
michael@0 | 1010 | bool useLocalCache = false; |
michael@0 | 1011 | |
michael@0 | 1012 | // get httpChannel to retrieve some info we need for nsIPluginStreamInfo setup |
michael@0 | 1013 | nsCOMPtr<nsIChannel> channel = do_QueryInterface(request); |
michael@0 | 1014 | nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel); |
michael@0 | 1015 | |
michael@0 | 1016 | /* |
michael@0 | 1017 | * Assumption |
michael@0 | 1018 | * By the time nsPluginStreamListenerPeer::OnDataAvailable() gets |
michael@0 | 1019 | * called, all the headers have been read. |
michael@0 | 1020 | */ |
michael@0 | 1021 | if (httpChannel) { |
michael@0 | 1022 | // Reassemble the HTTP response status line and provide it to our |
michael@0 | 1023 | // listener. Would be nice if we could get the raw status line, |
michael@0 | 1024 | // but nsIHttpChannel doesn't currently provide that. |
michael@0 | 1025 | // Status code: required; the status line isn't useful without it. |
michael@0 | 1026 | uint32_t statusNum; |
michael@0 | 1027 | if (NS_SUCCEEDED(httpChannel->GetResponseStatus(&statusNum)) && |
michael@0 | 1028 | statusNum < 1000) { |
michael@0 | 1029 | // HTTP version: provide if available. Defaults to empty string. |
michael@0 | 1030 | nsCString ver; |
michael@0 | 1031 | nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal = |
michael@0 | 1032 | do_QueryInterface(channel); |
michael@0 | 1033 | if (httpChannelInternal) { |
michael@0 | 1034 | uint32_t major, minor; |
michael@0 | 1035 | if (NS_SUCCEEDED(httpChannelInternal->GetResponseVersion(&major, |
michael@0 | 1036 | &minor))) { |
michael@0 | 1037 | ver = nsPrintfCString("/%lu.%lu", major, minor); |
michael@0 | 1038 | } |
michael@0 | 1039 | } |
michael@0 | 1040 | |
michael@0 | 1041 | // Status text: provide if available. Defaults to "OK". |
michael@0 | 1042 | nsCString statusText; |
michael@0 | 1043 | if (NS_FAILED(httpChannel->GetResponseStatusText(statusText))) { |
michael@0 | 1044 | statusText = "OK"; |
michael@0 | 1045 | } |
michael@0 | 1046 | |
michael@0 | 1047 | // Assemble everything and pass to listener. |
michael@0 | 1048 | nsPrintfCString status("HTTP%s %lu %s", ver.get(), statusNum, |
michael@0 | 1049 | statusText.get()); |
michael@0 | 1050 | static_cast<nsIHTTPHeaderListener*>(mPStreamListener)->StatusLine(status.get()); |
michael@0 | 1051 | } |
michael@0 | 1052 | |
michael@0 | 1053 | // Also provide all HTTP response headers to our listener. |
michael@0 | 1054 | httpChannel->VisitResponseHeaders(this); |
michael@0 | 1055 | |
michael@0 | 1056 | mSeekable = false; |
michael@0 | 1057 | // first we look for a content-encoding header. If we find one, we tell the |
michael@0 | 1058 | // plugin that stream is not seekable, because the plugin always sees |
michael@0 | 1059 | // uncompressed data, so it can't make meaningful range requests on a |
michael@0 | 1060 | // compressed entity. Also, we force the plugin to use |
michael@0 | 1061 | // nsPluginStreamType_AsFile stream type and we have to save decompressed |
michael@0 | 1062 | // file into local plugin cache, because necko cache contains original |
michael@0 | 1063 | // compressed file. |
michael@0 | 1064 | nsAutoCString contentEncoding; |
michael@0 | 1065 | if (NS_SUCCEEDED(httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Content-Encoding"), |
michael@0 | 1066 | contentEncoding))) { |
michael@0 | 1067 | useLocalCache = true; |
michael@0 | 1068 | } else { |
michael@0 | 1069 | // set seekability (seekable if the stream has a known length and if the |
michael@0 | 1070 | // http server accepts byte ranges). |
michael@0 | 1071 | uint32_t length; |
michael@0 | 1072 | GetLength(&length); |
michael@0 | 1073 | if (length) { |
michael@0 | 1074 | nsAutoCString range; |
michael@0 | 1075 | if (NS_SUCCEEDED(httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("accept-ranges"), range)) && |
michael@0 | 1076 | range.Equals(NS_LITERAL_CSTRING("bytes"), nsCaseInsensitiveCStringComparator())) { |
michael@0 | 1077 | mSeekable = true; |
michael@0 | 1078 | } |
michael@0 | 1079 | } |
michael@0 | 1080 | } |
michael@0 | 1081 | |
michael@0 | 1082 | // we require a content len |
michael@0 | 1083 | // get Last-Modified header for plugin info |
michael@0 | 1084 | nsAutoCString lastModified; |
michael@0 | 1085 | if (NS_SUCCEEDED(httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("last-modified"), lastModified)) && |
michael@0 | 1086 | !lastModified.IsEmpty()) { |
michael@0 | 1087 | PRTime time64; |
michael@0 | 1088 | PR_ParseTimeString(lastModified.get(), true, &time64); //convert string time to integer time |
michael@0 | 1089 | |
michael@0 | 1090 | // Convert PRTime to unix-style time_t, i.e. seconds since the epoch |
michael@0 | 1091 | double fpTime = double(time64); |
michael@0 | 1092 | mModified = (uint32_t)(fpTime * 1e-6 + 0.5); |
michael@0 | 1093 | } |
michael@0 | 1094 | } |
michael@0 | 1095 | |
michael@0 | 1096 | rv = mPStreamListener->OnStartBinding(this); |
michael@0 | 1097 | |
michael@0 | 1098 | mStartBinding = true; |
michael@0 | 1099 | |
michael@0 | 1100 | if (NS_FAILED(rv)) |
michael@0 | 1101 | return rv; |
michael@0 | 1102 | |
michael@0 | 1103 | mPStreamListener->GetStreamType(&mStreamType); |
michael@0 | 1104 | |
michael@0 | 1105 | if (!useLocalCache && mStreamType >= NP_ASFILE) { |
michael@0 | 1106 | // check it out if this is not a file channel. |
michael@0 | 1107 | nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(request); |
michael@0 | 1108 | if (!fileChannel) { |
michael@0 | 1109 | useLocalCache = true; |
michael@0 | 1110 | } |
michael@0 | 1111 | } |
michael@0 | 1112 | |
michael@0 | 1113 | if (useLocalCache) { |
michael@0 | 1114 | SetupPluginCacheFile(channel); |
michael@0 | 1115 | } |
michael@0 | 1116 | |
michael@0 | 1117 | return NS_OK; |
michael@0 | 1118 | } |
michael@0 | 1119 | |
michael@0 | 1120 | nsresult |
michael@0 | 1121 | nsPluginStreamListenerPeer::OnFileAvailable(nsIFile* aFile) |
michael@0 | 1122 | { |
michael@0 | 1123 | nsresult rv; |
michael@0 | 1124 | if (!mPStreamListener) |
michael@0 | 1125 | return NS_ERROR_FAILURE; |
michael@0 | 1126 | |
michael@0 | 1127 | nsAutoCString path; |
michael@0 | 1128 | rv = aFile->GetNativePath(path); |
michael@0 | 1129 | if (NS_FAILED(rv)) return rv; |
michael@0 | 1130 | |
michael@0 | 1131 | if (path.IsEmpty()) { |
michael@0 | 1132 | NS_WARNING("empty path"); |
michael@0 | 1133 | return NS_OK; |
michael@0 | 1134 | } |
michael@0 | 1135 | |
michael@0 | 1136 | rv = mPStreamListener->OnFileAvailable(this, path.get()); |
michael@0 | 1137 | return rv; |
michael@0 | 1138 | } |
michael@0 | 1139 | |
michael@0 | 1140 | NS_IMETHODIMP |
michael@0 | 1141 | nsPluginStreamListenerPeer::VisitHeader(const nsACString &header, const nsACString &value) |
michael@0 | 1142 | { |
michael@0 | 1143 | return mPStreamListener->NewResponseHeader(PromiseFlatCString(header).get(), |
michael@0 | 1144 | PromiseFlatCString(value).get()); |
michael@0 | 1145 | } |
michael@0 | 1146 | |
michael@0 | 1147 | nsresult |
michael@0 | 1148 | nsPluginStreamListenerPeer::GetInterfaceGlobal(const nsIID& aIID, void** result) |
michael@0 | 1149 | { |
michael@0 | 1150 | if (!mPluginInstance) { |
michael@0 | 1151 | return NS_ERROR_FAILURE; |
michael@0 | 1152 | } |
michael@0 | 1153 | |
michael@0 | 1154 | nsRefPtr<nsPluginInstanceOwner> owner = mPluginInstance->GetOwner(); |
michael@0 | 1155 | if (owner) { |
michael@0 | 1156 | nsCOMPtr<nsIDocument> doc; |
michael@0 | 1157 | nsresult rv = owner->GetDocument(getter_AddRefs(doc)); |
michael@0 | 1158 | if (NS_SUCCEEDED(rv) && doc) { |
michael@0 | 1159 | nsPIDOMWindow *window = doc->GetWindow(); |
michael@0 | 1160 | if (window) { |
michael@0 | 1161 | nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(window); |
michael@0 | 1162 | nsCOMPtr<nsIInterfaceRequestor> ir = do_QueryInterface(webNav); |
michael@0 | 1163 | return ir->GetInterface(aIID, result); |
michael@0 | 1164 | } |
michael@0 | 1165 | } |
michael@0 | 1166 | } |
michael@0 | 1167 | |
michael@0 | 1168 | return NS_ERROR_FAILURE; |
michael@0 | 1169 | } |
michael@0 | 1170 | |
michael@0 | 1171 | NS_IMETHODIMP |
michael@0 | 1172 | nsPluginStreamListenerPeer::GetInterface(const nsIID& aIID, void** result) |
michael@0 | 1173 | { |
michael@0 | 1174 | // Provide nsIChannelEventSink ourselves, otherwise let our document's |
michael@0 | 1175 | // script global object owner provide the interface. |
michael@0 | 1176 | if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) { |
michael@0 | 1177 | return QueryInterface(aIID, result); |
michael@0 | 1178 | } |
michael@0 | 1179 | |
michael@0 | 1180 | return GetInterfaceGlobal(aIID, result); |
michael@0 | 1181 | } |
michael@0 | 1182 | |
michael@0 | 1183 | /** |
michael@0 | 1184 | * Proxy class which forwards async redirect notifications back to the necko |
michael@0 | 1185 | * callback, keeping nsPluginStreamListenerPeer::mRequests in sync with |
michael@0 | 1186 | * which channel is active. |
michael@0 | 1187 | */ |
michael@0 | 1188 | class ChannelRedirectProxyCallback : public nsIAsyncVerifyRedirectCallback |
michael@0 | 1189 | { |
michael@0 | 1190 | public: |
michael@0 | 1191 | ChannelRedirectProxyCallback(nsPluginStreamListenerPeer* listener, |
michael@0 | 1192 | nsIAsyncVerifyRedirectCallback* parent, |
michael@0 | 1193 | nsIChannel* oldChannel, |
michael@0 | 1194 | nsIChannel* newChannel) |
michael@0 | 1195 | : mWeakListener(do_GetWeakReference(static_cast<nsIStreamListener*>(listener))) |
michael@0 | 1196 | , mParent(parent) |
michael@0 | 1197 | , mOldChannel(oldChannel) |
michael@0 | 1198 | , mNewChannel(newChannel) |
michael@0 | 1199 | { |
michael@0 | 1200 | } |
michael@0 | 1201 | |
michael@0 | 1202 | ChannelRedirectProxyCallback() {} |
michael@0 | 1203 | virtual ~ChannelRedirectProxyCallback() {} |
michael@0 | 1204 | |
michael@0 | 1205 | NS_DECL_ISUPPORTS |
michael@0 | 1206 | |
michael@0 | 1207 | NS_IMETHODIMP OnRedirectVerifyCallback(nsresult result) |
michael@0 | 1208 | { |
michael@0 | 1209 | if (NS_SUCCEEDED(result)) { |
michael@0 | 1210 | nsCOMPtr<nsIStreamListener> listener = do_QueryReferent(mWeakListener); |
michael@0 | 1211 | if (listener) |
michael@0 | 1212 | static_cast<nsPluginStreamListenerPeer*>(listener.get())->ReplaceRequest(mOldChannel, mNewChannel); |
michael@0 | 1213 | } |
michael@0 | 1214 | return mParent->OnRedirectVerifyCallback(result); |
michael@0 | 1215 | } |
michael@0 | 1216 | |
michael@0 | 1217 | private: |
michael@0 | 1218 | nsWeakPtr mWeakListener; |
michael@0 | 1219 | nsCOMPtr<nsIAsyncVerifyRedirectCallback> mParent; |
michael@0 | 1220 | nsCOMPtr<nsIChannel> mOldChannel; |
michael@0 | 1221 | nsCOMPtr<nsIChannel> mNewChannel; |
michael@0 | 1222 | }; |
michael@0 | 1223 | |
michael@0 | 1224 | NS_IMPL_ISUPPORTS(ChannelRedirectProxyCallback, nsIAsyncVerifyRedirectCallback) |
michael@0 | 1225 | |
michael@0 | 1226 | |
michael@0 | 1227 | NS_IMETHODIMP |
michael@0 | 1228 | nsPluginStreamListenerPeer::AsyncOnChannelRedirect(nsIChannel *oldChannel, nsIChannel *newChannel, |
michael@0 | 1229 | uint32_t flags, nsIAsyncVerifyRedirectCallback* callback) |
michael@0 | 1230 | { |
michael@0 | 1231 | // Disallow redirects if we don't have a stream listener. |
michael@0 | 1232 | if (!mPStreamListener) { |
michael@0 | 1233 | return NS_ERROR_FAILURE; |
michael@0 | 1234 | } |
michael@0 | 1235 | |
michael@0 | 1236 | nsCOMPtr<nsIAsyncVerifyRedirectCallback> proxyCallback = |
michael@0 | 1237 | new ChannelRedirectProxyCallback(this, callback, oldChannel, newChannel); |
michael@0 | 1238 | |
michael@0 | 1239 | // Give NPAPI a chance to control redirects. |
michael@0 | 1240 | bool notificationHandled = mPStreamListener->HandleRedirectNotification(oldChannel, newChannel, proxyCallback); |
michael@0 | 1241 | if (notificationHandled) { |
michael@0 | 1242 | return NS_OK; |
michael@0 | 1243 | } |
michael@0 | 1244 | |
michael@0 | 1245 | // Don't allow cross-origin 307 POST redirects. |
michael@0 | 1246 | nsCOMPtr<nsIHttpChannel> oldHttpChannel(do_QueryInterface(oldChannel)); |
michael@0 | 1247 | if (oldHttpChannel) { |
michael@0 | 1248 | uint32_t responseStatus; |
michael@0 | 1249 | nsresult rv = oldHttpChannel->GetResponseStatus(&responseStatus); |
michael@0 | 1250 | if (NS_FAILED(rv)) { |
michael@0 | 1251 | return rv; |
michael@0 | 1252 | } |
michael@0 | 1253 | if (responseStatus == 307) { |
michael@0 | 1254 | nsAutoCString method; |
michael@0 | 1255 | rv = oldHttpChannel->GetRequestMethod(method); |
michael@0 | 1256 | if (NS_FAILED(rv)) { |
michael@0 | 1257 | return rv; |
michael@0 | 1258 | } |
michael@0 | 1259 | if (method.EqualsLiteral("POST")) { |
michael@0 | 1260 | rv = nsContentUtils::CheckSameOrigin(oldChannel, newChannel); |
michael@0 | 1261 | if (NS_FAILED(rv)) { |
michael@0 | 1262 | return rv; |
michael@0 | 1263 | } |
michael@0 | 1264 | } |
michael@0 | 1265 | } |
michael@0 | 1266 | } |
michael@0 | 1267 | |
michael@0 | 1268 | // Fall back to channel event sink for window. |
michael@0 | 1269 | nsCOMPtr<nsIChannelEventSink> channelEventSink; |
michael@0 | 1270 | nsresult rv = GetInterfaceGlobal(NS_GET_IID(nsIChannelEventSink), getter_AddRefs(channelEventSink)); |
michael@0 | 1271 | if (NS_FAILED(rv)) { |
michael@0 | 1272 | return rv; |
michael@0 | 1273 | } |
michael@0 | 1274 | |
michael@0 | 1275 | return channelEventSink->AsyncOnChannelRedirect(oldChannel, newChannel, flags, proxyCallback); |
michael@0 | 1276 | } |