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 "nsNPAPIPluginStreamListener.h" |
michael@0 | 7 | #include "plstr.h" |
michael@0 | 8 | #include "prmem.h" |
michael@0 | 9 | #include "nsDirectoryServiceDefs.h" |
michael@0 | 10 | #include "nsDirectoryServiceUtils.h" |
michael@0 | 11 | #include "nsIFile.h" |
michael@0 | 12 | #include "nsNetUtil.h" |
michael@0 | 13 | #include "nsPluginHost.h" |
michael@0 | 14 | #include "nsNPAPIPlugin.h" |
michael@0 | 15 | #include "nsPluginLogging.h" |
michael@0 | 16 | #include "nsPluginStreamListenerPeer.h" |
michael@0 | 17 | |
michael@0 | 18 | #include <stdint.h> |
michael@0 | 19 | #include <algorithm> |
michael@0 | 20 | |
michael@0 | 21 | nsNPAPIStreamWrapper::nsNPAPIStreamWrapper(nsIOutputStream *outputStream, |
michael@0 | 22 | nsNPAPIPluginStreamListener *streamListener) |
michael@0 | 23 | { |
michael@0 | 24 | mOutputStream = outputStream; |
michael@0 | 25 | mStreamListener = streamListener; |
michael@0 | 26 | |
michael@0 | 27 | memset(&mNPStream, 0, sizeof(mNPStream)); |
michael@0 | 28 | mNPStream.ndata = static_cast<void*>(this); |
michael@0 | 29 | } |
michael@0 | 30 | |
michael@0 | 31 | nsNPAPIStreamWrapper::~nsNPAPIStreamWrapper() |
michael@0 | 32 | { |
michael@0 | 33 | if (mOutputStream) { |
michael@0 | 34 | mOutputStream->Close(); |
michael@0 | 35 | } |
michael@0 | 36 | } |
michael@0 | 37 | |
michael@0 | 38 | NS_IMPL_ISUPPORTS(nsPluginStreamToFile, nsIOutputStream) |
michael@0 | 39 | |
michael@0 | 40 | nsPluginStreamToFile::nsPluginStreamToFile(const char* target, |
michael@0 | 41 | nsIPluginInstanceOwner* owner) |
michael@0 | 42 | : mTarget(PL_strdup(target)), |
michael@0 | 43 | mOwner(owner) |
michael@0 | 44 | { |
michael@0 | 45 | nsresult rv; |
michael@0 | 46 | nsCOMPtr<nsIFile> pluginTmp; |
michael@0 | 47 | rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(pluginTmp)); |
michael@0 | 48 | if (NS_FAILED(rv)) return; |
michael@0 | 49 | |
michael@0 | 50 | mTempFile = do_QueryInterface(pluginTmp, &rv); |
michael@0 | 51 | if (NS_FAILED(rv)) return; |
michael@0 | 52 | |
michael@0 | 53 | // need to create a file with a unique name - use target as the basis |
michael@0 | 54 | rv = mTempFile->AppendNative(nsDependentCString(target)); |
michael@0 | 55 | if (NS_FAILED(rv)) return; |
michael@0 | 56 | |
michael@0 | 57 | // Yes, make it unique. |
michael@0 | 58 | rv = mTempFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0700); |
michael@0 | 59 | if (NS_FAILED(rv)) return; |
michael@0 | 60 | |
michael@0 | 61 | // create the file |
michael@0 | 62 | rv = NS_NewLocalFileOutputStream(getter_AddRefs(mOutputStream), mTempFile, -1, 00600); |
michael@0 | 63 | if (NS_FAILED(rv)) |
michael@0 | 64 | return; |
michael@0 | 65 | |
michael@0 | 66 | // construct the URL we'll use later in calls to GetURL() |
michael@0 | 67 | NS_GetURLSpecFromFile(mTempFile, mFileURL); |
michael@0 | 68 | |
michael@0 | 69 | #ifdef DEBUG |
michael@0 | 70 | printf("File URL = %s\n", mFileURL.get()); |
michael@0 | 71 | #endif |
michael@0 | 72 | } |
michael@0 | 73 | |
michael@0 | 74 | nsPluginStreamToFile::~nsPluginStreamToFile() |
michael@0 | 75 | { |
michael@0 | 76 | // should we be deleting mTempFile here? |
michael@0 | 77 | if (nullptr != mTarget) |
michael@0 | 78 | PL_strfree(mTarget); |
michael@0 | 79 | } |
michael@0 | 80 | |
michael@0 | 81 | NS_IMETHODIMP |
michael@0 | 82 | nsPluginStreamToFile::Flush() |
michael@0 | 83 | { |
michael@0 | 84 | return NS_OK; |
michael@0 | 85 | } |
michael@0 | 86 | |
michael@0 | 87 | NS_IMETHODIMP |
michael@0 | 88 | nsPluginStreamToFile::Write(const char* aBuf, uint32_t aCount, |
michael@0 | 89 | uint32_t *aWriteCount) |
michael@0 | 90 | { |
michael@0 | 91 | mOutputStream->Write(aBuf, aCount, aWriteCount); |
michael@0 | 92 | mOutputStream->Flush(); |
michael@0 | 93 | mOwner->GetURL(mFileURL.get(), mTarget, nullptr, nullptr, 0); |
michael@0 | 94 | |
michael@0 | 95 | return NS_OK; |
michael@0 | 96 | } |
michael@0 | 97 | |
michael@0 | 98 | NS_IMETHODIMP |
michael@0 | 99 | nsPluginStreamToFile::WriteFrom(nsIInputStream *inStr, uint32_t count, |
michael@0 | 100 | uint32_t *_retval) |
michael@0 | 101 | { |
michael@0 | 102 | NS_NOTREACHED("WriteFrom"); |
michael@0 | 103 | return NS_ERROR_NOT_IMPLEMENTED; |
michael@0 | 104 | } |
michael@0 | 105 | |
michael@0 | 106 | NS_IMETHODIMP |
michael@0 | 107 | nsPluginStreamToFile::WriteSegments(nsReadSegmentFun reader, void * closure, |
michael@0 | 108 | uint32_t count, uint32_t *_retval) |
michael@0 | 109 | { |
michael@0 | 110 | NS_NOTREACHED("WriteSegments"); |
michael@0 | 111 | return NS_ERROR_NOT_IMPLEMENTED; |
michael@0 | 112 | } |
michael@0 | 113 | |
michael@0 | 114 | NS_IMETHODIMP |
michael@0 | 115 | nsPluginStreamToFile::IsNonBlocking(bool *aNonBlocking) |
michael@0 | 116 | { |
michael@0 | 117 | *aNonBlocking = false; |
michael@0 | 118 | return NS_OK; |
michael@0 | 119 | } |
michael@0 | 120 | |
michael@0 | 121 | NS_IMETHODIMP |
michael@0 | 122 | nsPluginStreamToFile::Close(void) |
michael@0 | 123 | { |
michael@0 | 124 | mOutputStream->Close(); |
michael@0 | 125 | mOwner->GetURL(mFileURL.get(), mTarget, nullptr, nullptr, 0); |
michael@0 | 126 | return NS_OK; |
michael@0 | 127 | } |
michael@0 | 128 | |
michael@0 | 129 | // nsNPAPIPluginStreamListener Methods |
michael@0 | 130 | |
michael@0 | 131 | NS_IMPL_ISUPPORTS(nsNPAPIPluginStreamListener, |
michael@0 | 132 | nsITimerCallback, nsIHTTPHeaderListener) |
michael@0 | 133 | |
michael@0 | 134 | nsNPAPIPluginStreamListener::nsNPAPIPluginStreamListener(nsNPAPIPluginInstance* inst, |
michael@0 | 135 | void* notifyData, |
michael@0 | 136 | const char* aURL) |
michael@0 | 137 | : mStreamBuffer(nullptr), |
michael@0 | 138 | mNotifyURL(aURL ? PL_strdup(aURL) : nullptr), |
michael@0 | 139 | mInst(inst), |
michael@0 | 140 | mStreamBufferSize(0), |
michael@0 | 141 | mStreamBufferByteCount(0), |
michael@0 | 142 | mStreamType(NP_NORMAL), |
michael@0 | 143 | mStreamStarted(false), |
michael@0 | 144 | mStreamCleanedUp(false), |
michael@0 | 145 | mCallNotify(notifyData ? true : false), |
michael@0 | 146 | mIsSuspended(false), |
michael@0 | 147 | mIsPluginInitJSStream(mInst->mInPluginInitCall && |
michael@0 | 148 | aURL && strncmp(aURL, "javascript:", |
michael@0 | 149 | sizeof("javascript:") - 1) == 0), |
michael@0 | 150 | mRedirectDenied(false), |
michael@0 | 151 | mResponseHeaderBuf(nullptr) |
michael@0 | 152 | { |
michael@0 | 153 | mNPStreamWrapper = new nsNPAPIStreamWrapper(nullptr, this); |
michael@0 | 154 | mNPStreamWrapper->mNPStream.notifyData = notifyData; |
michael@0 | 155 | } |
michael@0 | 156 | |
michael@0 | 157 | nsNPAPIPluginStreamListener::~nsNPAPIPluginStreamListener() |
michael@0 | 158 | { |
michael@0 | 159 | // remove this from the plugin instance's stream list |
michael@0 | 160 | nsTArray<nsNPAPIPluginStreamListener*> *streamListeners = mInst->StreamListeners(); |
michael@0 | 161 | streamListeners->RemoveElement(this); |
michael@0 | 162 | |
michael@0 | 163 | // For those cases when NewStream is never called, we still may need |
michael@0 | 164 | // to fire a notification callback. Return network error as fallback |
michael@0 | 165 | // reason because for other cases, notify should have already been |
michael@0 | 166 | // called for other reasons elsewhere. |
michael@0 | 167 | CallURLNotify(NPRES_NETWORK_ERR); |
michael@0 | 168 | |
michael@0 | 169 | // lets get rid of the buffer |
michael@0 | 170 | if (mStreamBuffer) { |
michael@0 | 171 | PR_Free(mStreamBuffer); |
michael@0 | 172 | mStreamBuffer=nullptr; |
michael@0 | 173 | } |
michael@0 | 174 | |
michael@0 | 175 | if (mNotifyURL) |
michael@0 | 176 | PL_strfree(mNotifyURL); |
michael@0 | 177 | |
michael@0 | 178 | if (mResponseHeaderBuf) |
michael@0 | 179 | PL_strfree(mResponseHeaderBuf); |
michael@0 | 180 | |
michael@0 | 181 | if (mNPStreamWrapper) { |
michael@0 | 182 | delete mNPStreamWrapper; |
michael@0 | 183 | } |
michael@0 | 184 | } |
michael@0 | 185 | |
michael@0 | 186 | nsresult |
michael@0 | 187 | nsNPAPIPluginStreamListener::CleanUpStream(NPReason reason) |
michael@0 | 188 | { |
michael@0 | 189 | nsresult rv = NS_ERROR_FAILURE; |
michael@0 | 190 | |
michael@0 | 191 | // Various bits of code in the rest of this method may result in the |
michael@0 | 192 | // deletion of this object. Use a KungFuDeathGrip to keep ourselves |
michael@0 | 193 | // alive during cleanup. |
michael@0 | 194 | nsRefPtr<nsNPAPIPluginStreamListener> kungFuDeathGrip(this); |
michael@0 | 195 | |
michael@0 | 196 | if (mStreamCleanedUp) |
michael@0 | 197 | return NS_OK; |
michael@0 | 198 | |
michael@0 | 199 | mStreamCleanedUp = true; |
michael@0 | 200 | |
michael@0 | 201 | StopDataPump(); |
michael@0 | 202 | |
michael@0 | 203 | // Release any outstanding redirect callback. |
michael@0 | 204 | if (mHTTPRedirectCallback) { |
michael@0 | 205 | mHTTPRedirectCallback->OnRedirectVerifyCallback(NS_ERROR_FAILURE); |
michael@0 | 206 | mHTTPRedirectCallback = nullptr; |
michael@0 | 207 | } |
michael@0 | 208 | |
michael@0 | 209 | // Seekable streams have an extra addref when they are created which must |
michael@0 | 210 | // be matched here. |
michael@0 | 211 | if (NP_SEEK == mStreamType && mStreamStarted) |
michael@0 | 212 | NS_RELEASE_THIS(); |
michael@0 | 213 | |
michael@0 | 214 | if (mStreamListenerPeer) { |
michael@0 | 215 | mStreamListenerPeer->CancelRequests(NS_BINDING_ABORTED); |
michael@0 | 216 | mStreamListenerPeer = nullptr; |
michael@0 | 217 | } |
michael@0 | 218 | |
michael@0 | 219 | if (!mInst || !mInst->CanFireNotifications()) |
michael@0 | 220 | return rv; |
michael@0 | 221 | |
michael@0 | 222 | PluginDestructionGuard guard(mInst); |
michael@0 | 223 | |
michael@0 | 224 | nsNPAPIPlugin* plugin = mInst->GetPlugin(); |
michael@0 | 225 | if (!plugin || !plugin->GetLibrary()) |
michael@0 | 226 | return rv; |
michael@0 | 227 | |
michael@0 | 228 | NPPluginFuncs* pluginFunctions = plugin->PluginFuncs(); |
michael@0 | 229 | |
michael@0 | 230 | NPP npp; |
michael@0 | 231 | mInst->GetNPP(&npp); |
michael@0 | 232 | |
michael@0 | 233 | if (mStreamStarted && pluginFunctions->destroystream) { |
michael@0 | 234 | NPPAutoPusher nppPusher(npp); |
michael@0 | 235 | |
michael@0 | 236 | NPError error; |
michael@0 | 237 | NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->destroystream)(npp, &mNPStreamWrapper->mNPStream, reason), mInst, |
michael@0 | 238 | NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO); |
michael@0 | 239 | |
michael@0 | 240 | NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL, |
michael@0 | 241 | ("NPP DestroyStream called: this=%p, npp=%p, reason=%d, return=%d, url=%s\n", |
michael@0 | 242 | this, npp, reason, error, mNPStreamWrapper->mNPStream.url)); |
michael@0 | 243 | |
michael@0 | 244 | if (error == NPERR_NO_ERROR) |
michael@0 | 245 | rv = NS_OK; |
michael@0 | 246 | } |
michael@0 | 247 | |
michael@0 | 248 | mStreamStarted = false; |
michael@0 | 249 | |
michael@0 | 250 | // fire notification back to plugin, just like before |
michael@0 | 251 | CallURLNotify(reason); |
michael@0 | 252 | |
michael@0 | 253 | return rv; |
michael@0 | 254 | } |
michael@0 | 255 | |
michael@0 | 256 | void |
michael@0 | 257 | nsNPAPIPluginStreamListener::CallURLNotify(NPReason reason) |
michael@0 | 258 | { |
michael@0 | 259 | if (!mCallNotify || !mInst || !mInst->CanFireNotifications()) |
michael@0 | 260 | return; |
michael@0 | 261 | |
michael@0 | 262 | PluginDestructionGuard guard(mInst); |
michael@0 | 263 | |
michael@0 | 264 | mCallNotify = false; // only do this ONCE and prevent recursion |
michael@0 | 265 | |
michael@0 | 266 | nsNPAPIPlugin* plugin = mInst->GetPlugin(); |
michael@0 | 267 | if (!plugin || !plugin->GetLibrary()) |
michael@0 | 268 | return; |
michael@0 | 269 | |
michael@0 | 270 | NPPluginFuncs* pluginFunctions = plugin->PluginFuncs(); |
michael@0 | 271 | |
michael@0 | 272 | if (pluginFunctions->urlnotify) { |
michael@0 | 273 | NPP npp; |
michael@0 | 274 | mInst->GetNPP(&npp); |
michael@0 | 275 | |
michael@0 | 276 | NS_TRY_SAFE_CALL_VOID((*pluginFunctions->urlnotify)(npp, mNotifyURL, reason, mNPStreamWrapper->mNPStream.notifyData), mInst, |
michael@0 | 277 | NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO); |
michael@0 | 278 | |
michael@0 | 279 | NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL, |
michael@0 | 280 | ("NPP URLNotify called: this=%p, npp=%p, notify=%p, reason=%d, url=%s\n", |
michael@0 | 281 | this, npp, mNPStreamWrapper->mNPStream.notifyData, reason, mNotifyURL)); |
michael@0 | 282 | } |
michael@0 | 283 | } |
michael@0 | 284 | |
michael@0 | 285 | nsresult |
michael@0 | 286 | nsNPAPIPluginStreamListener::OnStartBinding(nsPluginStreamListenerPeer* streamPeer) |
michael@0 | 287 | { |
michael@0 | 288 | if (!mInst || !mInst->CanFireNotifications() || mStreamCleanedUp) |
michael@0 | 289 | return NS_ERROR_FAILURE; |
michael@0 | 290 | |
michael@0 | 291 | PluginDestructionGuard guard(mInst); |
michael@0 | 292 | |
michael@0 | 293 | nsNPAPIPlugin* plugin = mInst->GetPlugin(); |
michael@0 | 294 | if (!plugin || !plugin->GetLibrary()) |
michael@0 | 295 | return NS_ERROR_FAILURE; |
michael@0 | 296 | |
michael@0 | 297 | NPPluginFuncs* pluginFunctions = plugin->PluginFuncs(); |
michael@0 | 298 | |
michael@0 | 299 | if (!pluginFunctions->newstream) |
michael@0 | 300 | return NS_ERROR_FAILURE; |
michael@0 | 301 | |
michael@0 | 302 | NPP npp; |
michael@0 | 303 | mInst->GetNPP(&npp); |
michael@0 | 304 | |
michael@0 | 305 | bool seekable; |
michael@0 | 306 | char* contentType; |
michael@0 | 307 | uint16_t streamType = NP_NORMAL; |
michael@0 | 308 | NPError error; |
michael@0 | 309 | |
michael@0 | 310 | streamPeer->GetURL(&mNPStreamWrapper->mNPStream.url); |
michael@0 | 311 | streamPeer->GetLength((uint32_t*)&(mNPStreamWrapper->mNPStream.end)); |
michael@0 | 312 | streamPeer->GetLastModified((uint32_t*)&(mNPStreamWrapper->mNPStream.lastmodified)); |
michael@0 | 313 | streamPeer->IsSeekable(&seekable); |
michael@0 | 314 | streamPeer->GetContentType(&contentType); |
michael@0 | 315 | |
michael@0 | 316 | if (!mResponseHeaders.IsEmpty()) { |
michael@0 | 317 | mResponseHeaderBuf = PL_strdup(mResponseHeaders.get()); |
michael@0 | 318 | mNPStreamWrapper->mNPStream.headers = mResponseHeaderBuf; |
michael@0 | 319 | } |
michael@0 | 320 | |
michael@0 | 321 | mStreamListenerPeer = streamPeer; |
michael@0 | 322 | |
michael@0 | 323 | NPPAutoPusher nppPusher(npp); |
michael@0 | 324 | |
michael@0 | 325 | NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->newstream)(npp, (char*)contentType, &mNPStreamWrapper->mNPStream, seekable, &streamType), mInst, |
michael@0 | 326 | NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO); |
michael@0 | 327 | |
michael@0 | 328 | NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL, |
michael@0 | 329 | ("NPP NewStream called: this=%p, npp=%p, mime=%s, seek=%d, type=%d, return=%d, url=%s\n", |
michael@0 | 330 | this, npp, (char *)contentType, seekable, streamType, error, mNPStreamWrapper->mNPStream.url)); |
michael@0 | 331 | |
michael@0 | 332 | if (error != NPERR_NO_ERROR) |
michael@0 | 333 | return NS_ERROR_FAILURE; |
michael@0 | 334 | |
michael@0 | 335 | switch(streamType) |
michael@0 | 336 | { |
michael@0 | 337 | case NP_NORMAL: |
michael@0 | 338 | mStreamType = NP_NORMAL; |
michael@0 | 339 | break; |
michael@0 | 340 | case NP_ASFILEONLY: |
michael@0 | 341 | mStreamType = NP_ASFILEONLY; |
michael@0 | 342 | break; |
michael@0 | 343 | case NP_ASFILE: |
michael@0 | 344 | mStreamType = NP_ASFILE; |
michael@0 | 345 | break; |
michael@0 | 346 | case NP_SEEK: |
michael@0 | 347 | mStreamType = NP_SEEK; |
michael@0 | 348 | // Seekable streams should continue to exist even after OnStopRequest |
michael@0 | 349 | // is fired, so we AddRef ourself an extra time and Release when the |
michael@0 | 350 | // plugin calls NPN_DestroyStream (CleanUpStream). If the plugin never |
michael@0 | 351 | // calls NPN_DestroyStream the stream will be destroyed before the plugin |
michael@0 | 352 | // instance is destroyed. |
michael@0 | 353 | NS_ADDREF_THIS(); |
michael@0 | 354 | break; |
michael@0 | 355 | default: |
michael@0 | 356 | return NS_ERROR_FAILURE; |
michael@0 | 357 | } |
michael@0 | 358 | |
michael@0 | 359 | mStreamStarted = true; |
michael@0 | 360 | return NS_OK; |
michael@0 | 361 | } |
michael@0 | 362 | |
michael@0 | 363 | void |
michael@0 | 364 | nsNPAPIPluginStreamListener::SuspendRequest() |
michael@0 | 365 | { |
michael@0 | 366 | NS_ASSERTION(!mIsSuspended, |
michael@0 | 367 | "Suspending a request that's already suspended!"); |
michael@0 | 368 | |
michael@0 | 369 | nsresult rv = StartDataPump(); |
michael@0 | 370 | if (NS_FAILED(rv)) |
michael@0 | 371 | return; |
michael@0 | 372 | |
michael@0 | 373 | mIsSuspended = true; |
michael@0 | 374 | |
michael@0 | 375 | if (mStreamListenerPeer) { |
michael@0 | 376 | mStreamListenerPeer->SuspendRequests(); |
michael@0 | 377 | } |
michael@0 | 378 | } |
michael@0 | 379 | |
michael@0 | 380 | void |
michael@0 | 381 | nsNPAPIPluginStreamListener::ResumeRequest() |
michael@0 | 382 | { |
michael@0 | 383 | mStreamListenerPeer->ResumeRequests(); |
michael@0 | 384 | mIsSuspended = false; |
michael@0 | 385 | } |
michael@0 | 386 | |
michael@0 | 387 | nsresult |
michael@0 | 388 | nsNPAPIPluginStreamListener::StartDataPump() |
michael@0 | 389 | { |
michael@0 | 390 | nsresult rv; |
michael@0 | 391 | mDataPumpTimer = do_CreateInstance("@mozilla.org/timer;1", &rv); |
michael@0 | 392 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 393 | |
michael@0 | 394 | // Start pumping data to the plugin every 100ms until it obeys and |
michael@0 | 395 | // eats the data. |
michael@0 | 396 | return mDataPumpTimer->InitWithCallback(this, 100, |
michael@0 | 397 | nsITimer::TYPE_REPEATING_SLACK); |
michael@0 | 398 | } |
michael@0 | 399 | |
michael@0 | 400 | void |
michael@0 | 401 | nsNPAPIPluginStreamListener::StopDataPump() |
michael@0 | 402 | { |
michael@0 | 403 | if (mDataPumpTimer) { |
michael@0 | 404 | mDataPumpTimer->Cancel(); |
michael@0 | 405 | mDataPumpTimer = nullptr; |
michael@0 | 406 | } |
michael@0 | 407 | } |
michael@0 | 408 | |
michael@0 | 409 | // Return true if a javascript: load that was started while the plugin |
michael@0 | 410 | // was being initialized is still in progress. |
michael@0 | 411 | bool |
michael@0 | 412 | nsNPAPIPluginStreamListener::PluginInitJSLoadInProgress() |
michael@0 | 413 | { |
michael@0 | 414 | if (!mInst) |
michael@0 | 415 | return false; |
michael@0 | 416 | |
michael@0 | 417 | nsTArray<nsNPAPIPluginStreamListener*> *streamListeners = mInst->StreamListeners(); |
michael@0 | 418 | for (unsigned int i = 0; i < streamListeners->Length(); i++) { |
michael@0 | 419 | if (streamListeners->ElementAt(i)->mIsPluginInitJSStream) { |
michael@0 | 420 | return true; |
michael@0 | 421 | } |
michael@0 | 422 | } |
michael@0 | 423 | |
michael@0 | 424 | return false; |
michael@0 | 425 | } |
michael@0 | 426 | |
michael@0 | 427 | // This method is called when there's more data available off the |
michael@0 | 428 | // network, but it's also called from our data pump when we're feeding |
michael@0 | 429 | // the plugin data that we already got off the network, but the plugin |
michael@0 | 430 | // was unable to consume it at the point it arrived. In the case when |
michael@0 | 431 | // the plugin pump calls this method, the input argument will be null, |
michael@0 | 432 | // and the length will be the number of bytes available in our |
michael@0 | 433 | // internal buffer. |
michael@0 | 434 | nsresult |
michael@0 | 435 | nsNPAPIPluginStreamListener::OnDataAvailable(nsPluginStreamListenerPeer* streamPeer, |
michael@0 | 436 | nsIInputStream* input, |
michael@0 | 437 | uint32_t length) |
michael@0 | 438 | { |
michael@0 | 439 | if (!length || !mInst || !mInst->CanFireNotifications()) |
michael@0 | 440 | return NS_ERROR_FAILURE; |
michael@0 | 441 | |
michael@0 | 442 | PluginDestructionGuard guard(mInst); |
michael@0 | 443 | |
michael@0 | 444 | // Just in case the caller switches plugin info on us. |
michael@0 | 445 | mStreamListenerPeer = streamPeer; |
michael@0 | 446 | |
michael@0 | 447 | nsNPAPIPlugin* plugin = mInst->GetPlugin(); |
michael@0 | 448 | if (!plugin || !plugin->GetLibrary()) |
michael@0 | 449 | return NS_ERROR_FAILURE; |
michael@0 | 450 | |
michael@0 | 451 | NPPluginFuncs* pluginFunctions = plugin->PluginFuncs(); |
michael@0 | 452 | |
michael@0 | 453 | // check out if plugin implements NPP_Write call |
michael@0 | 454 | if (!pluginFunctions->write) |
michael@0 | 455 | return NS_ERROR_FAILURE; // it'll cancel necko transaction |
michael@0 | 456 | |
michael@0 | 457 | if (!mStreamBuffer) { |
michael@0 | 458 | // To optimize the mem usage & performance we have to allocate |
michael@0 | 459 | // mStreamBuffer here in first ODA when length of data available |
michael@0 | 460 | // in input stream is known. mStreamBuffer will be freed in DTOR. |
michael@0 | 461 | // we also have to remember the size of that buff to make safe |
michael@0 | 462 | // consecutive Read() calls form input stream into our buff. |
michael@0 | 463 | |
michael@0 | 464 | uint32_t contentLength; |
michael@0 | 465 | streamPeer->GetLength(&contentLength); |
michael@0 | 466 | |
michael@0 | 467 | mStreamBufferSize = std::max(length, contentLength); |
michael@0 | 468 | |
michael@0 | 469 | // Limit the size of the initial buffer to MAX_PLUGIN_NECKO_BUFFER |
michael@0 | 470 | // (16k). This buffer will grow if needed, as in the case where |
michael@0 | 471 | // we're getting data faster than the plugin can process it. |
michael@0 | 472 | mStreamBufferSize = std::min(mStreamBufferSize, |
michael@0 | 473 | uint32_t(MAX_PLUGIN_NECKO_BUFFER)); |
michael@0 | 474 | |
michael@0 | 475 | mStreamBuffer = (char*) PR_Malloc(mStreamBufferSize); |
michael@0 | 476 | if (!mStreamBuffer) |
michael@0 | 477 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 478 | } |
michael@0 | 479 | |
michael@0 | 480 | // prepare NPP_ calls params |
michael@0 | 481 | NPP npp; |
michael@0 | 482 | mInst->GetNPP(&npp); |
michael@0 | 483 | |
michael@0 | 484 | int32_t streamPosition; |
michael@0 | 485 | streamPeer->GetStreamOffset(&streamPosition); |
michael@0 | 486 | int32_t streamOffset = streamPosition; |
michael@0 | 487 | |
michael@0 | 488 | if (input) { |
michael@0 | 489 | streamOffset += length; |
michael@0 | 490 | |
michael@0 | 491 | // Set new stream offset for the next ODA call regardless of how |
michael@0 | 492 | // following NPP_Write call will behave we pretend to consume all |
michael@0 | 493 | // data from the input stream. It's possible that current steam |
michael@0 | 494 | // position will be overwritten from NPP_RangeRequest call made |
michael@0 | 495 | // from NPP_Write, so we cannot call SetStreamOffset after |
michael@0 | 496 | // NPP_Write. |
michael@0 | 497 | // |
michael@0 | 498 | // Note: there is a special case when data flow should be |
michael@0 | 499 | // temporarily stopped if NPP_WriteReady returns 0 (bug #89270) |
michael@0 | 500 | streamPeer->SetStreamOffset(streamOffset); |
michael@0 | 501 | |
michael@0 | 502 | // set new end in case the content is compressed |
michael@0 | 503 | // initial end is less than end of decompressed stream |
michael@0 | 504 | // and some plugins (e.g. acrobat) can fail. |
michael@0 | 505 | if ((int32_t)mNPStreamWrapper->mNPStream.end < streamOffset) |
michael@0 | 506 | mNPStreamWrapper->mNPStream.end = streamOffset; |
michael@0 | 507 | } |
michael@0 | 508 | |
michael@0 | 509 | nsresult rv = NS_OK; |
michael@0 | 510 | while (NS_SUCCEEDED(rv) && length > 0) { |
michael@0 | 511 | if (input && length) { |
michael@0 | 512 | if (mStreamBufferSize < mStreamBufferByteCount + length && mIsSuspended) { |
michael@0 | 513 | // We're in the ::OnDataAvailable() call that we might get |
michael@0 | 514 | // after suspending a request, or we suspended the request |
michael@0 | 515 | // from within this ::OnDataAvailable() call while there's |
michael@0 | 516 | // still data in the input, and we don't have enough space to |
michael@0 | 517 | // store what we got off the network. Reallocate our internal |
michael@0 | 518 | // buffer. |
michael@0 | 519 | mStreamBufferSize = mStreamBufferByteCount + length; |
michael@0 | 520 | char *buf = (char*)PR_Realloc(mStreamBuffer, mStreamBufferSize); |
michael@0 | 521 | if (!buf) |
michael@0 | 522 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 523 | |
michael@0 | 524 | mStreamBuffer = buf; |
michael@0 | 525 | } |
michael@0 | 526 | |
michael@0 | 527 | uint32_t bytesToRead = |
michael@0 | 528 | std::min(length, mStreamBufferSize - mStreamBufferByteCount); |
michael@0 | 529 | |
michael@0 | 530 | uint32_t amountRead = 0; |
michael@0 | 531 | rv = input->Read(mStreamBuffer + mStreamBufferByteCount, bytesToRead, |
michael@0 | 532 | &amountRead); |
michael@0 | 533 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 534 | |
michael@0 | 535 | if (amountRead == 0) { |
michael@0 | 536 | NS_NOTREACHED("input->Read() returns no data, it's almost impossible " |
michael@0 | 537 | "to get here"); |
michael@0 | 538 | |
michael@0 | 539 | break; |
michael@0 | 540 | } |
michael@0 | 541 | |
michael@0 | 542 | mStreamBufferByteCount += amountRead; |
michael@0 | 543 | length -= amountRead; |
michael@0 | 544 | } else { |
michael@0 | 545 | // No input, nothing to read. Set length to 0 so that we don't |
michael@0 | 546 | // keep iterating through this outer loop any more. |
michael@0 | 547 | |
michael@0 | 548 | length = 0; |
michael@0 | 549 | } |
michael@0 | 550 | |
michael@0 | 551 | // Temporary pointer to the beginning of the data we're writing as |
michael@0 | 552 | // we loop and feed the plugin data. |
michael@0 | 553 | char *ptrStreamBuffer = mStreamBuffer; |
michael@0 | 554 | |
michael@0 | 555 | // it is possible plugin's NPP_Write() returns 0 byte consumed. We |
michael@0 | 556 | // use zeroBytesWriteCount to count situation like this and break |
michael@0 | 557 | // the loop |
michael@0 | 558 | int32_t zeroBytesWriteCount = 0; |
michael@0 | 559 | |
michael@0 | 560 | // mStreamBufferByteCount tells us how many bytes there are in the |
michael@0 | 561 | // buffer. WriteReady returns to us how many bytes the plugin is |
michael@0 | 562 | // ready to handle. |
michael@0 | 563 | while (mStreamBufferByteCount > 0) { |
michael@0 | 564 | int32_t numtowrite; |
michael@0 | 565 | if (pluginFunctions->writeready) { |
michael@0 | 566 | NPPAutoPusher nppPusher(npp); |
michael@0 | 567 | |
michael@0 | 568 | NS_TRY_SAFE_CALL_RETURN(numtowrite, (*pluginFunctions->writeready)(npp, &mNPStreamWrapper->mNPStream), mInst, |
michael@0 | 569 | NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO); |
michael@0 | 570 | NPP_PLUGIN_LOG(PLUGIN_LOG_NOISY, |
michael@0 | 571 | ("NPP WriteReady called: this=%p, npp=%p, " |
michael@0 | 572 | "return(towrite)=%d, url=%s\n", |
michael@0 | 573 | this, npp, numtowrite, mNPStreamWrapper->mNPStream.url)); |
michael@0 | 574 | |
michael@0 | 575 | if (!mStreamStarted) { |
michael@0 | 576 | // The plugin called NPN_DestroyStream() from within |
michael@0 | 577 | // NPP_WriteReady(), kill the stream. |
michael@0 | 578 | |
michael@0 | 579 | return NS_BINDING_ABORTED; |
michael@0 | 580 | } |
michael@0 | 581 | |
michael@0 | 582 | // if WriteReady returned 0, the plugin is not ready to handle |
michael@0 | 583 | // the data, suspend the stream (if it isn't already |
michael@0 | 584 | // suspended). |
michael@0 | 585 | // |
michael@0 | 586 | // Also suspend the stream if the stream we're loading is not |
michael@0 | 587 | // a javascript: URL load that was initiated during plugin |
michael@0 | 588 | // initialization and there currently is such a stream |
michael@0 | 589 | // loading. This is done to work around a Windows Media Player |
michael@0 | 590 | // plugin bug where it can't deal with being fed data for |
michael@0 | 591 | // other streams while it's waiting for data from the |
michael@0 | 592 | // javascript: URL loads it requests during |
michael@0 | 593 | // initialization. See bug 386493 for more details. |
michael@0 | 594 | |
michael@0 | 595 | if (numtowrite <= 0 || |
michael@0 | 596 | (!mIsPluginInitJSStream && PluginInitJSLoadInProgress())) { |
michael@0 | 597 | if (!mIsSuspended) { |
michael@0 | 598 | SuspendRequest(); |
michael@0 | 599 | } |
michael@0 | 600 | |
michael@0 | 601 | // Break out of the inner loop, but keep going through the |
michael@0 | 602 | // outer loop in case there's more data to read from the |
michael@0 | 603 | // input stream. |
michael@0 | 604 | |
michael@0 | 605 | break; |
michael@0 | 606 | } |
michael@0 | 607 | |
michael@0 | 608 | numtowrite = std::min(numtowrite, mStreamBufferByteCount); |
michael@0 | 609 | } else { |
michael@0 | 610 | // if WriteReady is not supported by the plugin, just write |
michael@0 | 611 | // the whole buffer |
michael@0 | 612 | numtowrite = mStreamBufferByteCount; |
michael@0 | 613 | } |
michael@0 | 614 | |
michael@0 | 615 | NPPAutoPusher nppPusher(npp); |
michael@0 | 616 | |
michael@0 | 617 | int32_t writeCount = 0; // bytes consumed by plugin instance |
michael@0 | 618 | NS_TRY_SAFE_CALL_RETURN(writeCount, (*pluginFunctions->write)(npp, &mNPStreamWrapper->mNPStream, streamPosition, numtowrite, ptrStreamBuffer), mInst, |
michael@0 | 619 | NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO); |
michael@0 | 620 | |
michael@0 | 621 | NPP_PLUGIN_LOG(PLUGIN_LOG_NOISY, |
michael@0 | 622 | ("NPP Write called: this=%p, npp=%p, pos=%d, len=%d, " |
michael@0 | 623 | "buf=%s, return(written)=%d, url=%s\n", |
michael@0 | 624 | this, npp, streamPosition, numtowrite, |
michael@0 | 625 | ptrStreamBuffer, writeCount, mNPStreamWrapper->mNPStream.url)); |
michael@0 | 626 | |
michael@0 | 627 | if (!mStreamStarted) { |
michael@0 | 628 | // The plugin called NPN_DestroyStream() from within |
michael@0 | 629 | // NPP_Write(), kill the stream. |
michael@0 | 630 | return NS_BINDING_ABORTED; |
michael@0 | 631 | } |
michael@0 | 632 | |
michael@0 | 633 | if (writeCount > 0) { |
michael@0 | 634 | NS_ASSERTION(writeCount <= mStreamBufferByteCount, |
michael@0 | 635 | "Plugin read past the end of the available data!"); |
michael@0 | 636 | |
michael@0 | 637 | writeCount = std::min(writeCount, mStreamBufferByteCount); |
michael@0 | 638 | mStreamBufferByteCount -= writeCount; |
michael@0 | 639 | |
michael@0 | 640 | streamPosition += writeCount; |
michael@0 | 641 | |
michael@0 | 642 | zeroBytesWriteCount = 0; |
michael@0 | 643 | |
michael@0 | 644 | if (mStreamBufferByteCount > 0) { |
michael@0 | 645 | // This alignment code is most likely bogus, but we'll leave |
michael@0 | 646 | // it in for now in case it matters for some plugins on some |
michael@0 | 647 | // architectures. Who knows... |
michael@0 | 648 | if (writeCount % sizeof(intptr_t)) { |
michael@0 | 649 | // memmove will take care about alignment |
michael@0 | 650 | memmove(mStreamBuffer, ptrStreamBuffer + writeCount, |
michael@0 | 651 | mStreamBufferByteCount); |
michael@0 | 652 | ptrStreamBuffer = mStreamBuffer; |
michael@0 | 653 | } else { |
michael@0 | 654 | // if aligned we can use ptrStreamBuffer += to eliminate |
michael@0 | 655 | // memmove() |
michael@0 | 656 | ptrStreamBuffer += writeCount; |
michael@0 | 657 | } |
michael@0 | 658 | } |
michael@0 | 659 | } else if (writeCount == 0) { |
michael@0 | 660 | // if NPP_Write() returns writeCount == 0 lets say 3 times in |
michael@0 | 661 | // a row, suspend the request and continue feeding the plugin |
michael@0 | 662 | // the data we got so far. Once that data is consumed, we'll |
michael@0 | 663 | // resume the request. |
michael@0 | 664 | if (mIsSuspended || ++zeroBytesWriteCount == 3) { |
michael@0 | 665 | if (!mIsSuspended) { |
michael@0 | 666 | SuspendRequest(); |
michael@0 | 667 | } |
michael@0 | 668 | |
michael@0 | 669 | // Break out of the for loop, but keep going through the |
michael@0 | 670 | // while loop in case there's more data to read from the |
michael@0 | 671 | // input stream. |
michael@0 | 672 | |
michael@0 | 673 | break; |
michael@0 | 674 | } |
michael@0 | 675 | } else { |
michael@0 | 676 | // Something's really wrong, kill the stream. |
michael@0 | 677 | rv = NS_ERROR_FAILURE; |
michael@0 | 678 | |
michael@0 | 679 | break; |
michael@0 | 680 | } |
michael@0 | 681 | } // end of inner while loop |
michael@0 | 682 | |
michael@0 | 683 | if (mStreamBufferByteCount && mStreamBuffer != ptrStreamBuffer) { |
michael@0 | 684 | memmove(mStreamBuffer, ptrStreamBuffer, mStreamBufferByteCount); |
michael@0 | 685 | } |
michael@0 | 686 | } |
michael@0 | 687 | |
michael@0 | 688 | if (streamPosition != streamOffset) { |
michael@0 | 689 | // The plugin didn't consume all available data, or consumed some |
michael@0 | 690 | // of our cached data while we're pumping cached data. Adjust the |
michael@0 | 691 | // plugin info's stream offset to match reality, except if the |
michael@0 | 692 | // plugin info's stream offset was set by a re-entering |
michael@0 | 693 | // NPN_RequestRead() call. |
michael@0 | 694 | |
michael@0 | 695 | int32_t postWriteStreamPosition; |
michael@0 | 696 | streamPeer->GetStreamOffset(&postWriteStreamPosition); |
michael@0 | 697 | |
michael@0 | 698 | if (postWriteStreamPosition == streamOffset) { |
michael@0 | 699 | streamPeer->SetStreamOffset(streamPosition); |
michael@0 | 700 | } |
michael@0 | 701 | } |
michael@0 | 702 | |
michael@0 | 703 | return rv; |
michael@0 | 704 | } |
michael@0 | 705 | |
michael@0 | 706 | nsresult |
michael@0 | 707 | nsNPAPIPluginStreamListener::OnFileAvailable(nsPluginStreamListenerPeer* streamPeer, |
michael@0 | 708 | const char* fileName) |
michael@0 | 709 | { |
michael@0 | 710 | if (!mInst || !mInst->CanFireNotifications()) |
michael@0 | 711 | return NS_ERROR_FAILURE; |
michael@0 | 712 | |
michael@0 | 713 | PluginDestructionGuard guard(mInst); |
michael@0 | 714 | |
michael@0 | 715 | nsNPAPIPlugin* plugin = mInst->GetPlugin(); |
michael@0 | 716 | if (!plugin || !plugin->GetLibrary()) |
michael@0 | 717 | return NS_ERROR_FAILURE; |
michael@0 | 718 | |
michael@0 | 719 | NPPluginFuncs* pluginFunctions = plugin->PluginFuncs(); |
michael@0 | 720 | |
michael@0 | 721 | if (!pluginFunctions->asfile) |
michael@0 | 722 | return NS_ERROR_FAILURE; |
michael@0 | 723 | |
michael@0 | 724 | NPP npp; |
michael@0 | 725 | mInst->GetNPP(&npp); |
michael@0 | 726 | |
michael@0 | 727 | NS_TRY_SAFE_CALL_VOID((*pluginFunctions->asfile)(npp, &mNPStreamWrapper->mNPStream, fileName), mInst, |
michael@0 | 728 | NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO); |
michael@0 | 729 | |
michael@0 | 730 | NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL, |
michael@0 | 731 | ("NPP StreamAsFile called: this=%p, npp=%p, url=%s, file=%s\n", |
michael@0 | 732 | this, npp, mNPStreamWrapper->mNPStream.url, fileName)); |
michael@0 | 733 | |
michael@0 | 734 | return NS_OK; |
michael@0 | 735 | } |
michael@0 | 736 | |
michael@0 | 737 | nsresult |
michael@0 | 738 | nsNPAPIPluginStreamListener::OnStopBinding(nsPluginStreamListenerPeer* streamPeer, |
michael@0 | 739 | nsresult status) |
michael@0 | 740 | { |
michael@0 | 741 | StopDataPump(); |
michael@0 | 742 | |
michael@0 | 743 | if (NS_FAILED(status)) { |
michael@0 | 744 | // The stream was destroyed, or died for some reason. Make sure we |
michael@0 | 745 | // cancel the underlying request. |
michael@0 | 746 | if (mStreamListenerPeer) { |
michael@0 | 747 | mStreamListenerPeer->CancelRequests(status); |
michael@0 | 748 | } |
michael@0 | 749 | } |
michael@0 | 750 | |
michael@0 | 751 | if (!mInst || !mInst->CanFireNotifications()) |
michael@0 | 752 | return NS_ERROR_FAILURE; |
michael@0 | 753 | |
michael@0 | 754 | NPReason reason = NS_FAILED(status) ? NPRES_NETWORK_ERR : NPRES_DONE; |
michael@0 | 755 | if (mRedirectDenied || status == NS_BINDING_ABORTED) { |
michael@0 | 756 | reason = NPRES_USER_BREAK; |
michael@0 | 757 | } |
michael@0 | 758 | |
michael@0 | 759 | // The following code can result in the deletion of 'this'. Don't |
michael@0 | 760 | // assume we are alive after this! |
michael@0 | 761 | // |
michael@0 | 762 | // Delay cleanup if the stream is of type NP_SEEK and status isn't |
michael@0 | 763 | // NS_BINDING_ABORTED (meaning the plugin hasn't called NPN_DestroyStream). |
michael@0 | 764 | // This is because even though we're done delivering data the plugin may |
michael@0 | 765 | // want to seek. Eventually either the plugin will call NPN_DestroyStream |
michael@0 | 766 | // or we'll perform cleanup when the instance goes away. See bug 91140. |
michael@0 | 767 | if (mStreamType != NP_SEEK || |
michael@0 | 768 | (NP_SEEK == mStreamType && NS_BINDING_ABORTED == status)) { |
michael@0 | 769 | return CleanUpStream(reason); |
michael@0 | 770 | } |
michael@0 | 771 | |
michael@0 | 772 | return NS_OK; |
michael@0 | 773 | } |
michael@0 | 774 | |
michael@0 | 775 | nsresult |
michael@0 | 776 | nsNPAPIPluginStreamListener::GetStreamType(int32_t *result) |
michael@0 | 777 | { |
michael@0 | 778 | *result = mStreamType; |
michael@0 | 779 | return NS_OK; |
michael@0 | 780 | } |
michael@0 | 781 | |
michael@0 | 782 | NS_IMETHODIMP |
michael@0 | 783 | nsNPAPIPluginStreamListener::Notify(nsITimer *aTimer) |
michael@0 | 784 | { |
michael@0 | 785 | NS_ASSERTION(aTimer == mDataPumpTimer, "Uh, wrong timer?"); |
michael@0 | 786 | |
michael@0 | 787 | int32_t oldStreamBufferByteCount = mStreamBufferByteCount; |
michael@0 | 788 | |
michael@0 | 789 | nsresult rv = OnDataAvailable(mStreamListenerPeer, nullptr, mStreamBufferByteCount); |
michael@0 | 790 | |
michael@0 | 791 | if (NS_FAILED(rv)) { |
michael@0 | 792 | // We ran into an error, no need to keep firing this timer then. |
michael@0 | 793 | aTimer->Cancel(); |
michael@0 | 794 | return NS_OK; |
michael@0 | 795 | } |
michael@0 | 796 | |
michael@0 | 797 | if (mStreamBufferByteCount != oldStreamBufferByteCount && |
michael@0 | 798 | ((mStreamStarted && mStreamBufferByteCount < 1024) || |
michael@0 | 799 | mStreamBufferByteCount == 0)) { |
michael@0 | 800 | // The plugin read some data and we've got less than 1024 bytes in |
michael@0 | 801 | // our buffer (or its empty and the stream is already |
michael@0 | 802 | // done). Resume the request so that we get more data off the |
michael@0 | 803 | // network. |
michael@0 | 804 | ResumeRequest(); |
michael@0 | 805 | // Necko will pump data now that we've resumed the request. |
michael@0 | 806 | StopDataPump(); |
michael@0 | 807 | } |
michael@0 | 808 | |
michael@0 | 809 | return NS_OK; |
michael@0 | 810 | } |
michael@0 | 811 | |
michael@0 | 812 | NS_IMETHODIMP |
michael@0 | 813 | nsNPAPIPluginStreamListener::StatusLine(const char* line) |
michael@0 | 814 | { |
michael@0 | 815 | mResponseHeaders.Append(line); |
michael@0 | 816 | mResponseHeaders.Append('\n'); |
michael@0 | 817 | return NS_OK; |
michael@0 | 818 | } |
michael@0 | 819 | |
michael@0 | 820 | NS_IMETHODIMP |
michael@0 | 821 | nsNPAPIPluginStreamListener::NewResponseHeader(const char* headerName, |
michael@0 | 822 | const char* headerValue) |
michael@0 | 823 | { |
michael@0 | 824 | mResponseHeaders.Append(headerName); |
michael@0 | 825 | mResponseHeaders.Append(": "); |
michael@0 | 826 | mResponseHeaders.Append(headerValue); |
michael@0 | 827 | mResponseHeaders.Append('\n'); |
michael@0 | 828 | return NS_OK; |
michael@0 | 829 | } |
michael@0 | 830 | |
michael@0 | 831 | bool |
michael@0 | 832 | nsNPAPIPluginStreamListener::HandleRedirectNotification(nsIChannel *oldChannel, nsIChannel *newChannel, |
michael@0 | 833 | nsIAsyncVerifyRedirectCallback* callback) |
michael@0 | 834 | { |
michael@0 | 835 | nsCOMPtr<nsIHttpChannel> oldHttpChannel = do_QueryInterface(oldChannel); |
michael@0 | 836 | nsCOMPtr<nsIHttpChannel> newHttpChannel = do_QueryInterface(newChannel); |
michael@0 | 837 | if (!oldHttpChannel || !newHttpChannel) { |
michael@0 | 838 | return false; |
michael@0 | 839 | } |
michael@0 | 840 | |
michael@0 | 841 | if (!mInst || !mInst->CanFireNotifications()) { |
michael@0 | 842 | return false; |
michael@0 | 843 | } |
michael@0 | 844 | |
michael@0 | 845 | nsNPAPIPlugin* plugin = mInst->GetPlugin(); |
michael@0 | 846 | if (!plugin || !plugin->GetLibrary()) { |
michael@0 | 847 | return false; |
michael@0 | 848 | } |
michael@0 | 849 | |
michael@0 | 850 | NPPluginFuncs* pluginFunctions = plugin->PluginFuncs(); |
michael@0 | 851 | if (!pluginFunctions->urlredirectnotify) { |
michael@0 | 852 | return false; |
michael@0 | 853 | } |
michael@0 | 854 | |
michael@0 | 855 | // A non-null closure is required for redirect handling support. |
michael@0 | 856 | if (mNPStreamWrapper->mNPStream.notifyData) { |
michael@0 | 857 | uint32_t status; |
michael@0 | 858 | if (NS_SUCCEEDED(oldHttpChannel->GetResponseStatus(&status))) { |
michael@0 | 859 | nsCOMPtr<nsIURI> uri; |
michael@0 | 860 | if (NS_SUCCEEDED(newHttpChannel->GetURI(getter_AddRefs(uri))) && uri) { |
michael@0 | 861 | nsAutoCString spec; |
michael@0 | 862 | if (NS_SUCCEEDED(uri->GetAsciiSpec(spec))) { |
michael@0 | 863 | // At this point the plugin will be responsible for making the callback |
michael@0 | 864 | // so save the callback object. |
michael@0 | 865 | mHTTPRedirectCallback = callback; |
michael@0 | 866 | |
michael@0 | 867 | NPP npp; |
michael@0 | 868 | mInst->GetNPP(&npp); |
michael@0 | 869 | #if defined(XP_WIN) |
michael@0 | 870 | NS_TRY_SAFE_CALL_VOID((*pluginFunctions->urlredirectnotify)(npp, spec.get(), static_cast<int32_t>(status), mNPStreamWrapper->mNPStream.notifyData), mInst, |
michael@0 | 871 | NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO); |
michael@0 | 872 | #else |
michael@0 | 873 | MAIN_THREAD_JNI_REF_GUARD; |
michael@0 | 874 | (*pluginFunctions->urlredirectnotify)(npp, spec.get(), static_cast<int32_t>(status), mNPStreamWrapper->mNPStream.notifyData); |
michael@0 | 875 | #endif |
michael@0 | 876 | return true; |
michael@0 | 877 | } |
michael@0 | 878 | } |
michael@0 | 879 | } |
michael@0 | 880 | } |
michael@0 | 881 | |
michael@0 | 882 | callback->OnRedirectVerifyCallback(NS_ERROR_FAILURE); |
michael@0 | 883 | return true; |
michael@0 | 884 | } |
michael@0 | 885 | |
michael@0 | 886 | void |
michael@0 | 887 | nsNPAPIPluginStreamListener::URLRedirectResponse(NPBool allow) |
michael@0 | 888 | { |
michael@0 | 889 | if (mHTTPRedirectCallback) { |
michael@0 | 890 | mHTTPRedirectCallback->OnRedirectVerifyCallback(allow ? NS_OK : NS_ERROR_FAILURE); |
michael@0 | 891 | mRedirectDenied = allow ? false : true; |
michael@0 | 892 | mHTTPRedirectCallback = nullptr; |
michael@0 | 893 | } |
michael@0 | 894 | } |
michael@0 | 895 | |
michael@0 | 896 | void* |
michael@0 | 897 | nsNPAPIPluginStreamListener::GetNotifyData() |
michael@0 | 898 | { |
michael@0 | 899 | if (mNPStreamWrapper) { |
michael@0 | 900 | return mNPStreamWrapper->mNPStream.notifyData; |
michael@0 | 901 | } |
michael@0 | 902 | return nullptr; |
michael@0 | 903 | } |